def trace_runpage( use_display_list, doc, number): page = mupdf.Page( doc, number-1) mediabox = page.bound_page() print( f'<page number="{number}" mediabox="{mediabox.x0} {mediabox.y0} {mediabox.x1} {mediabox.y1}">') output = mupdf.Output( mupdf.Output.Fixed_STDOUT) dev = mupdf.Device( output) if use_display_list: list_ = mupdf.DisplayList( page) list_.run_display_list( dev, mupdf.Matrix(mupdf.fz_identity), mupdf.Rect(mupdf.fz_infinite_rect), mupdf.Cookie()) else: page.run( dev, mupdf.Matrix(mupdf.fz_identity), mupdf.Cookie()) output.close_output() print( '</page>')
def get_page(pno, zoom=False, max_size=None): """Return a tkinter.PhotoImage or a PNG image for a document page number. :arg int pno: 0-based page number :arg zoom: top-left of old clip rect, and one of -1, 0, +1 for dim. x or y to indicate the arrow key pressed :arg max_size: (width, height) of available image area :arg bool first: if True, we cannot use tkinter """ dlist = dlist_tab[pno] # get display list of page number if not dlist: # create if not yet there dlist_tab[pno] = doc.new_display_list_from_page_number(pno) dlist = dlist_tab[pno] r = dlist.bound_display_list() # the page rectangle clip = r # ensure image fits screen: # exploit, but do not exceed width or height zoom_0 = 1 if max_size: zoom_0 = min(1, max_size[0] / (r.x1 - r.x0), max_size[1] / (r.y1 - r.y0)) if zoom_0 == 1: zoom_0 = min(max_size[0] / (r.x1 - r.x0), max_size[1] / (r.y1 - r.y0)) mat_0 = mupdf.Matrix.scale(zoom_0, zoom_0) if not zoom: # show the total page pix = dlist.new_pixmap_from_display_list( mat_0, mupdf.Colorspace(mupdf.Colorspace.Fixed_RGB), alpha=0) else: w2 = r.width / 2 # we need these ... h2 = r.height / 2 # a few times clip = r * 0.5 # clip rect size is a quarter page tl = zoom[0] # old top-left tl.x += zoom[1] * (w2 / 2) # adjust topl-left ... tl.x = max(0, tl.x) # according to ... tl.x = min(w2, tl.x) # arrow key ... tl.y += zoom[2] * (h2 / 2) # provided, but ... tl.y = max(0, tl.y) # stay within ... tl.y = min(h2, tl.y) # the page rect clip = mupdf.Rect(tl, tl.x + w2, tl.y + h2) # fixme: this looks wrong. # clip rect is ready, now fill it mat = mat_0 * mupdf.Matrix(2, 2) # zoom matrix pix = dlist.new_pixmap_from_display_list( mat, mupdf.Colorspace(mupdf.Colorspace.Fixed_RGB), alpha=0) size = pix.pixmap_stride() * pix.h() buffer_ = mupdf.Buffer(size) output = mupdf.Output(buffer_) output.write_pixmap_as_pnm(pix) img = buffer_.buffer_extract() return img, (clip.x0, clip.y0) # return image, clip position
def convert_to_pdf(document, page_from=0, page_to=-1, rotate=0): num_pages = document.count_pages() page_from = max(page_from, 0) page_from = min(page_from, num_pages - 1) if page_to < 0: page_to = num_pages - 1 page_to = min(page_to, num_pages - 1) page_delta = 1 if page_to < page_from: p = page_to page_to = page_from page_from = p page_delta = -1 document_out = mupdf.PdfDocument() while rotate < 0: rotate += 360 while rotate >= 360: rotate -= 360 if rotate % 90 != 0: rotate = 0 for p in range(page_from, page_to + page_delta, page_delta): page = document.load_page(p) rect = page.bound_page() dev, resources, contents = document_out.page_write(rect) page.run_page(dev, mupdf.Matrix(), mupdf.Cookie()) pdf_obj = document_out.add_page(rect, rotate, resources, contents) document_out.insert_page(-1, pdf_obj) write_options = mupdf.PdfWriteOptions() write_options.do_garbage = 4 write_options.do_compress = 1 write_options.do_compress_images = 1 write_options.do_compress_fonts = 1 write_options.do_sanitize = 1 write_options.do_incremental = 0 write_options.do_ascii = 0 write_options.do_decompress = 0 write_options.do_linear = 0 write_options.do_clean = 1 write_options.do_pretty = 0 buffer_ = mupdf.Buffer(8192) output = mupdf.Output(buffer_) document_out.write_document(output, write_options) size, data = buffer_.buffer_extract_raw() print(f'buffer_.buffer_extract() returned: {size, data}') return data, size
def test(path): ''' Runs various mupdf operations on <path>, which is assumed to be a file that mupdf can open. ''' log(f'testing path={path}') assert os.path.isfile(path) global g_test_n g_test_n += 1 # See notes in mupdfwrap.py:build_swig() about buffer_extract() and # buffer_storage(). # assert getattr(mupdf.Buffer, 'buffer_storage_raw') assert getattr(mupdf.Buffer, 'buffer_storage', None) is None assert getattr(mupdf.Buffer, 'buffer_extract_raw') assert getattr(mupdf.Buffer, 'buffer_extract') # Test operations using functions: # log('Testing functions.') log(f' Opening: %s' % path) document = mupdf.open_document(path) log(f' mupdf.needs_password(document)={mupdf.needs_password(document)}') log(f' mupdf.count_pages(document)={mupdf.count_pages(document)}') log(f' mupdf.document_output_intent(document)={mupdf.document_output_intent(document)}' ) # Test operations using classes: # log(f'Testing classes') document = mupdf.Document(path) log(f'Have created mupdf.Document for {path}') log(f'document.needs_password()={document.needs_password()}') log(f'document.count_pages()={document.count_pages()}') if 0: log(f'stext info:') show_stext(document) for k in ( 'format', 'encryption', 'info:Author', 'info:Title', 'info:Creator', 'info:Producer', 'qwerty', ): v = document.lookup_metadata(k) log(f'document.lookup_metadata() k={k} returned v={v!r}') if k == 'qwerty': assert v is None, f'v={v!r}' else: pass zoom = 10 scale = mupdf.Matrix.scale(zoom / 100., zoom / 100.) page_number = 0 log(f'Have created scale: a={scale.a} b={scale.b} c={scale.c} d={scale.d} e={scale.e} f={scale.f}' ) colorspace = mupdf.Colorspace(mupdf.Colorspace.Fixed_RGB) log(f'{colorspace.m_internal.key_storable.storable.refs}') if 0: c = colorspace.clamp_color([3.14]) log('colorspace.clamp_color returned c={c}') pixmap = mupdf.Pixmap(document, page_number, scale, colorspace, 0) log(f'Have created pixmap: {pixmap.m_internal.w} {pixmap.m_internal.h} {pixmap.m_internal.stride} {pixmap.m_internal.n}' ) filename = f'mupdf_test-out1-{g_test_n}.png' pixmap.save_pixmap_as_png(filename) log(f'Have created {filename} using pixmap.save_pixmap_as_png().') # Print image data in ascii PPM format. Copied from # mupdf/docs/examples/example.c. # samples = pixmap.samples() stride = pixmap.stride() n = pixmap.n() filename = f'mupdf_test-out2-{g_test_n}.ppm' with open(filename, 'w') as f: f.write('P3\n') f.write('%s %s\n' % (pixmap.m_internal.w, pixmap.m_internal.h)) f.write('255\n') for y in range(0, pixmap.m_internal.h): for x in range(pixmap.m_internal.w): if x: f.write(' ') offset = y * stride + x * n if hasattr(mupdf, 'bytes_getitem'): # swig f.write('%3d %3d %3d' % ( mupdf.bytes_getitem(samples, offset + 0), mupdf.bytes_getitem(samples, offset + 1), mupdf.bytes_getitem(samples, offset + 2), )) else: # cppyy f.write('%3d %3d %3d' % ( samples[offset + 0], samples[offset + 1], samples[offset + 2], )) f.write('\n') log(f'Have created {filename} by scanning pixmap.') # Generate .png and but create Pixmap from Page instead of from Document. # page = mupdf.Page(document, 0) separations = page.page_separations() log(f'page_separations() returned {"true" if separations else "false"}') pixmap = mupdf.Pixmap(page, scale, colorspace, 0) filename = f'mupdf_test-out3-{g_test_n}.png' pixmap.save_pixmap_as_png(filename) log(f'Have created {filename} using pixmap.save_pixmap_as_png()') # Show links log(f'Links.') page = mupdf.Page(document, 0) link = mupdf.load_links(page.m_internal) log(f'{link}') if link: for i in link: log(f'{i}') # Check we can iterate over Link's, by creating one manually. # link = mupdf.Link(mupdf.Rect(0, 0, 1, 1), "hello") log(f'items in <link> are:') for i in link: log(f' {i.m_internal.refs} {i.m_internal.uri}') # Check iteration over Outlines. # log(f'Outlines.') outline = mupdf.Outline(document) log(f'outline.m_internal={outline.m_internal}') if outline.m_internal: log(f'{outline.uri()} {outline.page()} {outline.x()} {outline.y()} {outline.is_open()} {outline.title()}' ) log(f'items in outline tree are:') for o in outline: log(f' {o.uri()} {o.page()} {o.x()} {o.y()} {o.is_open()} {o.title()}' ) # Check iteration over StextPage. # log(f'StextPage.') stext_options = mupdf.StextOptions(0) page_num = 40 try: stext_page = mupdf.StextPage(document, page_num, stext_options) except Exception: log(f'no page_num={page_num}') else: device_stext = mupdf.Device(stext_page, stext_options) matrix = mupdf.Matrix() page = mupdf.Page(document, 0) cookie = mupdf.Cookie() page.run_page(device_stext, matrix, cookie) log(f' stext_page is:') for block in stext_page: log(f' block:') for line in block: line_text = '' for char in line: line_text += chr(char.m_internal.c) log(f' {line_text}') device_stext.close_device() # Check copy-constructor. log(f'Checking copy-constructor') document2 = mupdf.Document(document) del document page = mupdf.Page(document2, 0) scale = mupdf.Matrix() pixmap = mupdf.Pixmap(page, scale, colorspace, 0) pixmap.save_pixmap_as_png('mupdf_test-out3.png') stdout = mupdf.Output(mupdf.Output.Fixed_STDOUT) log(f'{type(stdout)} {stdout.m_internal.state}') mediabox = page.bound_page() out = mupdf.DocumentWriter(filename, 'png', '', mupdf.DocumentWriter.FormatPathType_DOCUMENT) dev = out.begin_page(mediabox) page.run_page(dev, mupdf.Matrix(mupdf.fz_identity), mupdf.Cookie()) out.end_page() # Check out-params are converted into python return value. bitmap = mupdf.Bitmap(10, 20, 8, 72, 72) bitmap_details = bitmap.bitmap_details() log(f'{bitmap_details}') assert list(bitmap_details) == [10, 20, 8, 12], f'bitmap_details={bitmap_details!r}' log(f'finished test of %s' % path)
def draw(argv): password = '' info = trace_info() items, argv = getopt.getopt( argv, 'qp:o:F:R:r:w:h:fB:c:e:G:Is:A:DiW:H:S:T:U:XLvPl:y:NO:a') for option, value in items: if 0: pass elif option == '-q': state.quiet = 1 elif option == '-p': password = value elif option == '-o': state.output = value elif option == '-F': state.format_ = value elif option == '-R': state.rotation = float(value) elif option == '-r': state.resolution = float(value) state.res_specified = 1 elif option == '-w': state.width = float(value) elif option == '-h': state.height = float(value) elif option == '-f': state.fit = 1 elif option == '-B': state.band_height = int(value) elif option == '-c': state.out_cs = parse_colorspace(value) elif option == '-e': state.proof_filename = value elif option == '-G': state.gamma_value = float(value) elif option == '-I': state.invert += 1 elif option == '-W': state.layout_w = float(value) elif option == '-H': state.layout_h = float(value) elif option == '-S': state.layout_em = float(value) elif option == '-U': state.layout_css = value elif option == '-X': state.layout_use_doc_css = 0 elif option == '-O': state.spots = float(value) if not mupdf.FZ_ENABLE_SPOT_RENDERING: sys.stderr.write( 'Spot rendering/Overprint/Overprint simulation not enabled in this build\n' ) state.spots = SPOTS_NONE elif option == '-s': if 't' in value: state.showtime += 1 if 'm' in value: state.showmemory += 1 if 'f' in value: state.showfeatures += 1 if '5' in value: state.showmd5 += 1 elif option == '-A': state.alphabits_graphics = int(value) sep = value.find('/') if sep >= 0: state.alphabits_text = int(value[sep + 1:]) else: state.alphabits_text = state.alphabits_graphics elif option == '-D': state.uselist = 0 elif option == '-l': state.min_line_width = float(value) elif option == '-i': state.ignore_errors = 1 elif option == '-N': state.no_icc = 1 elif option == '-T': state.num_workers = int(value) elif option == '-L': state.lowmemory = 1 elif option == '-P': bgprint.active = 1 elif option == '-y': state.layer_config = value elif option == '-a': state.useaccel = 0 elif option == '-v': sys.stderr.write(f'mudraw version {mupdf.FZ_VERSION}\n') if not argv: usage() if state.num_workers > 0: if state.uselist == 0: sys.stderr.write( 'cannot use multiple threads without using display list\n') sys.exit(1) if state.band_height == 0: sys.stderr.write( 'Using multiple threads without banding is pointless\n') if bgprint.active: if state.uselist == 0: sys.stderr.write('cannot bgprint without using display list\n') sys.exit(1) if state.proof_filename: proof_buffer = mupdf.Buffer(state.proof_filename) state.proof_cs = mupdf.Colorspace(FZ_COLORSPACE_NONE, 0, None, proof_buffer) mupdf.set_text_aa_level(state.alphabits_text) mupdf.set_graphics_aa_level(state.alphabits_graphics) mupdf.set_graphics_min_line_width(state.min_line_width) if state.no_icc: mupdf.disable_icc() else: mupdf.enable_icc() if state.layout_css: buf = mupdf.Buffer(state.layout_css) mupdf.set_user_css(buf.string_from_buffer()) mupdf.set_use_document_css(state.layout_use_doc_css) # Determine output type if state.band_height < 0: sys.stderr.write('Bandheight must be > 0\n') sys.exit(1) state.output_format = OUT_PNG if state.format_: for i in range(len(suffix_table)): if state.format_ == suffix_table[i].suffix[1:]: state.output_format = suffix_table[i].format if state.spots == SPOTS_FULL and suffix_table[i].spots == 0: sys.stderr.write( f'Output format {suffix_table[i].suffix[1:]} does not support spot rendering.\nDoing overprint simulation instead.\n' ) state.spots = SPOTS_OVERPRINT_SIM break else: sys.stderr.write(f'Unknown output format {format}\n') sys.exit(1) elif state.output: suffix = state.output i = 0 while 1: if i == len(suffix_table): break s = suffix.find(suffix_table[i].suffix) if s != -1: suffix = suffix_table[i].suffix[s + 1:] state.output_format = suffix_table[i].format if state.spots == SPOTS_FULL and suffix_table[i].spots == 0: sys.stderr.write( 'Output format {suffix_table[i].suffix[1:]} does not support spot rendering\nDoing overprint simulation instead.\n' ) state.spots = SPOTS_OVERPRINT_SIM i = 0 else: i += 1 if state.band_height: if state.output_format not in (OUT_PAM, OUT_PGM, OUT_PPM, OUT_PNM, OUT_PNG, OUT_PBM, OUT_PKM, OUT_PCL, OUT_PCLM, OUT_PS, OUT_PSD): sys.stderr.write( 'Banded operation only possible with PxM, PCL, PCLM, PS, PSD, and PNG outputs\n' ) sys.exit(1) if state.showmd5: sys.stderr.write('Banded operation not compatible with MD5\n') sys.exit(1) for i in range(len(format_cs_table)): if format_cs_table[i].format == state.output_format: if state.out_cs == CS_UNSET: state.out_cs = format_cs_table[i].default_cs for j in range(len(format_cs_table[i].permitted_cs)): if format_cs_table[i].permitted_cs[j] == state.out_cs: break else: sys.stderr.write('Unsupported colorspace for this format\n') sys.exit(1) state.alpha = 1 if state.out_cs in (CS_MONO, CS_GRAY, CS_GRAY_ALPHA): state.colorspace = mupdf.Colorspace(mupdf.Colorspace.Fixed_GRAY) state.alpha = (state.out_cs == CS_GRAY_ALPHA) elif state.out_cs in (CS_RGB, CS_RGB_ALPHA): state.colorspace = mupdf.Colorspace(mupdf.Colorspace.Fixed_RGB) state.alpha = (state.out_cs == CS_RGB_ALPHA) elif state.out_cs in (CS_CMYK, CS_CMYK_ALPHA): state.colorspace = mupdf.Colorspace(mupdf.Colorspace.Fixed_CMYK) state.alpha = (state.out_cs == CS_CMYK_ALPHA) elif state.out_cs == CS_ICC: try: icc_buffer = mupdf.Buffer(state.icc_filename) state.colorspace = Colorspace(mupdf.FZ_COLORSPACE_NONE, 0, None, icc_buffer) except Exception as e: sys.stderr.write('Invalid ICC destination color space\n') sys.exit(1) if state.colorspace.m_internal is None: sys.stderr.write('Invalid ICC destination color space\n') sys.exit(1) state.alpha = 0 else: sys.stderr.write('Unknown colorspace!\n') sys.exit(1) if state.out_cs != CS_ICC: state.colorspace = mupdf.Colorspace(state.colorspace) else: # Check to make sure this icc profile is ok with the output format */ okay = 0 for i in range(len(format_cs_table)): if format_cs_table[i].format == state.output_format: for j in range(len(format_cs_table[i].permitted_cs)): x = format_cs_table[i].permitted_cs[j] if x in (CS_MONO, CS_GRAY, CS_GRAY_ALPHA): if state.colorspace.colorspace_is_gray(): okay = 1 elif x in (CS_RGB, CS_RGB_ALPHA): if state.colorspace.colorspace_is_rgb(): okay = 1 elif x in (CS_CMYK, CS_CMYK_ALPHA): if state.colorspace.colorspace_is_cmyk(): okay = 1 if not okay: sys.stderr.write( 'ICC profile uses a colorspace that cannot be used for this format\n' ) sys.exit(1) if state.output_format == OUT_SVG: # SVG files are always opened for each page. Do not open "output". pass elif state.output and (not state.output.startswith('-') or len(state.output) >= 2) and len(state.output) >= 1: if has_percent_d(state.output): state.output_file_per_page = 1 else: state.out = mupdf.Output(state.output, 0) else: state.quiet = 1 # automatically be quiet if printing to stdout if 0: # Windows specific code to make stdout binary. if state.output_format not in (OUT_TEXT, OUT_STEXT, OUT_HTML, OUT_XHTML, OUT_TRACE, OUT_XMLTEXT): setmode(fileno(stdout), O_BINARY) state.out = mupdf.Output(mupdf.Output.Fixed_STDOUT) state.filename = argv[0] if not state.output_file_per_page: file_level_headers() timing.count = 0 timing.total = 0 timing.min = 1 << 30 timing.max = 0 timing.mininterp = 1 << 30 timing.maxinterp = 0 timing.minpage = 0 timing.maxpage = 0 timing.minfilename = "" timing.maxfilename = "" timing.layout = 0 timing.minlayout = 1 << 30 timing.maxlayout = 0 timing.minlayoutfilename = "" timing.maxlayoutfilename = "" if state.showtime and bgprint.active: timing.total = gettime() fz_optind = 0 try: while fz_optind < len(argv): try: accel = None state.filename = argv[fz_optind] fz_optind += 1 state.files += 1 if not state.useaccel: accel = None # If there was an accelerator to load, what would it be called? else: accelpath = get_accelerator_filename(state.filename) # Check whether that file exists, and isn't older than # the document. atime = stat_mtime(accelpath) dtime = stat_mtime(state.filename) if atime == 0: # No accelerator pass elif atime > dtime: accel = accelpath else: # Accelerator data is out of date os.unlink(accelpath) accel = None # In case we have jumped up from below # Unfortunately if accel=None, SWIG doesn't seem to think of it # as a char*, so we end up in fz_open_document_with_stream(). # # If we try to avoid this by setting accel='', SWIG correctly # calls Document(const char *filename, const char *accel) => # fz_open_accelerated_document(), but the latter function tests # for NULL not "" so fails. # # So we choose the constructor explicitly rather than leaving # it up to SWIG. # if accel: doc = mupdf.Document(state.filename, accel) else: doc = mupdf.Document(state.filename) if doc.needs_password(): if not doc.authenticate_password(password): raise Exception( f'cannot authenticate password: {state.filename}') # Once document is open check for output intent colorspace state.oi = doc.document_output_intent() if state.oi.m_internal: # See if we had explicitly set a profile to render if state.out_cs != CS_ICC: # In this case, we want to render to the output intent # color space if the number of channels is the same if state.oi.colorspace_n( ) == state.colorspace.colorspace_n(): state.colorspace = state.oi layouttime = time.time() doc.layout_document(state.layout_w, state.layout_h, state.layout_em) doc.count_pages() layouttime = time.time() - layouttime timing.layout += layouttime if layouttime < timing.minlayout: timing.minlayout = layouttime timing.minlayoutfilename = state.filename if layouttime > timing.maxlayout: timing.maxlayout = layouttime timing.maxlayoutfilename = state.filename if state.layer_config: apply_layer_config(doc, state.layer_config) if fz_optind == len(argv) or not mupdf.is_page_range( argv[fz_optind]): drawrange(doc, "1-N") if fz_optind < len(argv) and mupdf.is_page_range( argv[fz_optind]): drawrange(doc, argv[fz_optind]) fz_optind += 1 bgprint_flush() if state.useaccel: save_accelerator(doc, state.filename) except Exception as e: if not state.ignore_errors: raise bgprint_flush() sys.stderr.write(f'ignoring error in {state.filename}\n') except Exception as e: bgprint_flush() sys.stderr.write( f'error: cannot draw \'{state.filename}\' because: {e}\n') state.errored = 1 if not state.output_file_per_page: file_level_trailers() if state.out: state.out.close_output() state.out = None if state.showtime and timing.count > 0: if bgprint.active: timing.total = gettime() - timing.total if state.files == 1: sys.stderr.write( f'total {timing.total:.0f}ms ({timing.layout:.0f}ms layout) / {timing.count} pages for an average of {timing.total / timing.count:.0f}ms\n' ) if bgprint.active: sys.stderr.write( f'fastest page {timing.minpage}: {timing.mininterp:.0f}ms (interpretation) {timing.min - timing.mininterp:.0f}ms (rendering) {timing.min:.0f}ms(total)\n' ) sys.stderr.write( f'slowest page {timing.maxpage}: {timing.maxinterp:.0f}ms (interpretation) {timing.max - timing.maxinterp:.0f}ms (rendering) {timing.max:.0f}ms(total)\n' ) else: sys.stderr.write( f'fastest page {timing.minpage}: {timing.min:.0f}ms\n') sys.stderr.write( f'slowest page {timing.maxpage}: {timing.max:.0f}ms\n') else: sys.stderr.write( f'total {timing.total:.0f}ms ({timing.layout:.0f}ms layout) / {timing.count} pages for an average of {timing.total / timing.count:.0f}ms in {state.files} files\n' ) sys.stderr.write( f'fastest layout: {timing.minlayout:.0f}ms ({timing.minlayoutfilename})\n' ) sys.stderr.write( f'slowest layout: {timing.maxlayout:.0f}ms ({timing.maxlayoutfilename})\n' ) sys.stderr.write( f'fastest page {timing.minpage}: {timing.min:.0f}ms ({timing.minfilename})\n' ) sys.stderr.write( f'slowest page {timing.maxpage}: {timing.max:.0f}ms ({timing.maxfilename})\n' ) if state.showmemory: sys.stderr.write( f'Memory use total={info.total} peak={info.peak} current={info.current}\n' ) return state.errored != 0
def drawpage(doc, pagenum): list_ = None cookie = mupdf.Cookie() seps = None features = "" start = gettime() if state.showtime else 0 page = mupdf.Page(doc, pagenum - 1) if state.spots != SPOTS_NONE: seps = page.page_separations() if seps.m_internal: n = seps.count_separations() if state.spots == SPOTS_FULL: for i in range(n): seps.set_separation_behavior(i, mupdf.FZ_SEPARATION_SPOT) else: for i in range(n): seps.set_separation_behavior(i, mupdf.FZ_SEPARATION_COMPOSITE) elif page.page_uses_overprint(): # This page uses overprint, so we need an empty # sep object to force the overprint simulation on. seps = mupdf.Separations(0) elif state.oi and state.oi.m_internal and state.oi.colorspace_n( ) != state.colorspace.colorspace_n(): # We have an output intent, and it's incompatible # with the colorspace our device needs. Force the # overprint simulation on, because this ensures that # we 'simulate' the output intent too. */ seps = mupdf.Separations(0) if state.uselist: list_ = mupdf.DisplayList(page.bound_page()) dev = mupdf.Device(list_) if state.lowmemory: dev.enable_device_hints(FZ_NO_CACHE) page.run(dev, mupdf.Matrix(), cookie) dev.close_device() if bgprint.active and state.showtime: end = gettime() start = end - start if state.showfeatures: # SWIG doesn't appear to handle the out-param is_color in # mupdf.Device() constructor that wraps fz_new_test_device(), so we use # the underlying mupdf function() instead. # dev, iscolor = mupdf.new_test_device(0.02, 0, None) dev = mupdf.Device(dev) if state.lowmemory: dev.enable_device_hints(mupdf.FZ_NO_CACHE) if list_: list_.run_display_list(dev, mupdf.Matrix(mupdf.fz_identity), mupdf.Rect(mupdf.fz_infinite_rect), mupdf.Cookie()) else: page.run(dev, fz_identity, cookie) dev.close_device() features = " color" if iscolor else " grayscale" if state.output_file_per_page: bgprint_flush() if state.out: state.out.close_output() text_buffer = mupdf.format_output_path(state.output, pagenum) state.out = mupdf.Output(text_buffer, 0) if bgprint.active: bgprint_flush() if bgprint.active: if not state.quiet or state.showfeatures or state.showtime or state.showmd5: sys.stderr.write("page %s %d%s" % (state.filename, pagenum, features)) bgprint.started = 1 bgprint.page = page bgprint.list = list_ bgprint.seps = seps bgprint.filename = state.filename bgprint.pagenum = pagenum bgprint.interptime = start else: if not state.quiet or state.showfeatures or state.showtime or state.showmd5: sys.stderr.write("page %s %d%s" % (state.filename, pagenum, features)) dodrawpage(page, list_, pagenum, cookie, start, 0, state.filename, 0, seps)
def dodrawpage(page, list_, pagenum, cookie, start, interptime, filename, bg, seps): if state.output_file_per_page: file_level_headers() if list_: mediabox = mupdf.Rect(list_) else: mediabox = page.bound_page() if state.output_format == OUT_TRACE: state.out.write_string( "<page mediabox=\"%g %g %g %g\">\n" % (mediabox.x0, mediabox.y0, mediabox.x1, mediabox.y1)) dev = mupdf.Device(state.out) if state.lowmemory: dev.enable_device_hints(mupdf.FZ_NO_CACHE) if list_: list_.run_display_list(dev, mupdf.Matrix(), mupdf.Rect(mupdf.fz_infinite_rect), cookie) else: page.run(dev, fz_identity, cookie) state.out.write_string("</page>\n") dev.close_device() dev = None elif state.output_format == OUT_XMLTEXT: state.out.write_string( "<page mediabox=\"%g %g %g %g\">\n" % (mediabox.x0, mediabox.y0, mediabox.x1, mediabox.y1)) dev = mupdf.Device.new_raw_device(state.out) if list_: list_.run_display_list(dev, mupdf.Matrix(), mupdf.Rect(mupdf.fz_infinite_rect), cookie) else: page.run(dev, fz_identity, cookie) state.out.write_string("</page>\n") dev.close_device() dev = None elif state.output_format == OUT_BBOX: bbox = mupdf.Rect(mupdf.Rect.Fixed_EMPTY) dev = mupdf.Device(bbox) if state.lowmemory: dev.enable_device_hints(mupdf.FZ_NO_CACHE) if list_: list_.run_display_list(dev, fz_identity, mupdf.Rect(mupdf.fz_infinite_rect), cookie) else: page.run(dev, fz_identity, cookie) dev.close_device() state.out.write_string( "<page bbox=\"%s %s %s %s\" mediabox=\"%s %s %s %s\" />\n", bbox.x0, bbox.y0, bbox.x1, bbox.y1, mediabox.x0, mediabox.y0, mediabox.x1, mediabox.y1, ) elif state.output_format in (OUT_TEXT, OUT_HTML, OUT_XHTML, OUT_STEXT): zoom = state.resolution / 72 ctm = mupdf.Matrix( mupdf.pre_scale(mupdf.rotate(state.rotation), zoom, zoom)) stext_options = mupdf.StextOptions() stext_options.flags = mupdf.FZ_STEXT_PRESERVE_IMAGES if ( state.output_format == OUT_HTML or state.output_format == OUT_XHTML) else 0 text = mupdf.StextPage(mediabox) dev = mupdf.Device(text, stext_options) if state.lowmemory: fz_enable_device_hints(dev, FZ_NO_CACHE) if list_: list_.run_display_list(dev, ctm, mupdf.Rect(mupdf.fz_infinite_rect), cookie) else: page.run(dev, ctm, cookie) dev.close_device() dev = None if state.output_format == OUT_STEXT: state.out.print_stext_page_as_xml(text, pagenum) elif state.output_format == OUT_HTML: state.out.print_stext_page_as_html(text, pagenum) elif state.output_format == OUT_XHTML: state.out.print_stext_page_as_xhtml(text, pagenum) elif state.output_format == OUT_TEXT: state.out.print_stext_page_as_text(text) state.out.write_string("\f\n") elif state.output_format == OUT_SVG: zoom = state.resolution / 72 ctm = mupdf.Matrix(zoom, zoom) ctm.pre_rotate(state.rotation) tbounds = mupdf.Rect(mediabox, ctm) if not state.output or state.output == "-": state.out = mupdf.Output(mupdf.Output.Fixed_STDOUT) else: buf = mupdf.format_output_path(state.output, pagenum) state.out = mupdf.Output(buf, 0) dev = mupdf.Device(state.out, tbounds.x1 - tbounds.x0, tbounds.y1 - tbounds.y0, mupdf.FZ_SVG_TEXT_AS_PATH, 1) if state.lowmemory: dev.enable_device_hints(dev, mupdf.FZ_NO_CACHE) if list_: list_.run_display_list(dev, ctm, tbounds, cookie) else: page.run(dev, ctm, cookie) dev.close_device() state.out.close_output() else: zoom = state.resolution / 72 m = mupdf.rotate(state.rotation) ctm = mupdf.Matrix( mupdf.pre_scale(mupdf.rotate(state.rotation), zoom, zoom)) tbounds = mupdf.Rect(mediabox, ctm) ibounds = tbounds.round_rect() # Make local copies of our width/height w = state.width h = state.height # If a resolution is specified, check to see whether w/h are # exceeded; if not, unset them. */ if state.res_specified: t = ibounds.x1 - ibounds.x0 if w and t <= w: w = 0 t = ibounds.y1 - ibounds.y0 if h and t <= h: h = 0 # Now w or h will be 0 unless they need to be enforced. if w or h: scalex = w / (tbounds.x1 - tbounds.x0) scaley = h / (tbounds.y1 - tbounds.y0) if state.fit: if w == 0: scalex = 1.0 if h == 0: scaley = 1.0 else: if w == 0: scalex = scaley if h == 0: scaley = scalex if not state.fit: if scalex > scaley: scalex = scaley else: scaley = scalex scale_mat = mupdf.Matrix.scale(scalex, scaley) ctm = mupdf.Matrix( mupdf.concat(ctm.internal(), scale_mat.internal())) tbounds = mupdf.Rect(mediabox, ctm) ibounds = tbounds.round_rect() tbounds = ibounds.rect_from_irect() band_ibounds = ibounds bands = 1 totalheight = ibounds.y1 - ibounds.y0 drawheight = totalheight if state.band_height != 0: # Banded rendering; we'll only render to a # given height at a time. drawheight = state.band_height if totalheight > state.band_height: band_ibounds.y1 = band_ibounds.y0 + state.band_height bands = (totalheight + state.band_height - 1) / state.band_height tbounds.y1 = tbounds.y0 + state.band_height + 2 #DEBUG_THREADS(("Using %d Bands\n", bands)); if state.num_workers > 0: for band in range(min(state.num_workers, bands)): state.workers[band].band = band state.workers[band].ctm = ctm state.workers[band].tbounds = tbounds state.workers[band].cookie = mupdf.Cookie() state.workers[band].list = list_ state.workers[band].pix = mupdf.Pixmap(state.colorspace, band_ibounds, seps, state.alpha) state.workers[band].pix.set_pixmap_resolution( state.resolution, state.resolution) ctm.f -= drawheight pix = state.workers[0].pix else: pix = mupdf.Pixmap(state.colorspace, band_ibounds, seps, state.alpha) pix.set_pixmap_resolution(int(state.resolution), int(state.resolution)) # Output any page level headers (for banded formats) if state.output: state.bander = None if state.output_format == OUT_PGM or state.output_format == OUT_PPM or state.output_format == OUT_PNM: state.bander = mupdf.BandWriter(state.out, mupdf.BandWriter.PNM) elif state.output_format == OUT_PAM: state.bander = mupdf.BandWriter(state.out, mupdf.BandWriter.PAM) elif state.output_format == OUT_PNG: state.bander = mupdf.BandWriter(state.out, mupdf.BandWriter.PNG) elif state.output_format == OUT_PBM: state.bander = mupdf.BandWriter(state.out, mupdf.BandWriter.PBM) elif state.output_format == OUT_PKM: state.bander = mupdf.BandWriter(state.out, mupdf.BandWriter.PKM) elif state.output_format == OUT_PS: state.bander = mupdf.BandWriter(state.out, mupdf.BandWriter.PS) elif state.output_format == OUT_PSD: state.bander = mupdf.BandWriter(state.out, mupdf.BandWriter.PSD) elif state.output_format == OUT_PWG: if state.out_cs == CS_MONO: state.bander = mupdf.BandWriter(state.out, mupdf.BandWriter.MONO, mupdf.PwgOptions()) else: state.bander = mupdf.BandWriter(state.out, mupdf.BandWriter.COLOR, mupdf.PwgOptions()) elif state.output_format == OUT_PCL: if state.out_cs == CS_MONO: state.bander = mupdf.BandWriter(state.out, mupdf.BandWriter.MONO, mupdf.PclOptions()) else: state.bander = mupdf.BandWriter(state.out, mupdf.BandWriter.COLOR, mupdf.PclOptions()) if state.bander: state.bander.write_header(pix.w(), totalheight, pix.n(), pix.alpha(), pix.xres(), pix.yres(), state.output_pagenum, pix.colorspace(), pix.seps()) state.output_pagenum += 1 for band in range(bands): if state.num_workers > 0: w = state.workers[band % state.num_workers] pix = w.pix bit = w.bit w.bit = None cookie.increment_errors(w.cookie.errors()) else: bit = drawband(page, list_, ctm, tbounds, cookie, band * state.band_height, pix) if state.output: if state.bander: if bit: state.bander.write_band(bit.stride(), drawheight, bit.samples()) else: state.bander.write_band(pix.stride(), drawheight, pix.samples()) bit = None if state.num_workers > 0 and band + state.num_workers < bands: w = state.workers[band % state.num_workers] w.band = band + state.num_workers w.ctm = ctm w.tbounds = tbounds w.cookie = mupdf.Cookie() ctm.f -= drawheight # FIXME if state.showmd5: digest = pix.md5_pixmap() sys.stderr.write(' ') for i in range(16): sys.stderr.write('%02x', digest[i]) if state.output_file_per_page: file_level_trailers() if state.showtime: end = gettime() diff = end - start if bg: if diff + interptime < timing.min: timing.min = diff + interptime timing.mininterp = interptime timing.minpage = pagenum timing.minfilename = filename if diff + interptime > timing.max: timing.max = diff + interptime timing.maxinterp = interptime timing.maxpage = pagenum timing.maxfilename = filename timing.count += 1 sys.stderr.write( " %dms (interpretation) %dms (rendering) %dms (total)" % (interptime, diff, diff + interptime)) else: if diff < timing.min: timing.min = diff timing.minpage = pagenum timing.minfilename = filename if diff > timing.max: timing.max = diff timing.maxpage = pagenum timing.maxfilename = filename timing.total += diff timing.count += 1 sys.stderr.write(" %dms" % diff) if not state.quiet or state.showfeatures or state.showtime or state.showmd5: sys.stderr.write("\n") if state.lowmemory: mupdf.empty_store() if state.showmemory: mupdf.dump_glyph_cache_stats(mupdf.stderr_()) mupdf.flush_warnings() if cookie.get_errors(): state.errored = 1
def draw(argv): global alphabits_graphics global alphabits_text global band_height global colorspace global files global fir global format_ global gamma_value global height global ignore_errors global invert global layer_config global layout_css global layout_em global layout_h global layout_use_doc_css global layout_w global lowmemory global min_line_width global no_icc global num_workers global num_workers global out global out_cs global output global output_file_per_page global output_format global password global proof_filename global quiet global resolution global res_specified global rotation global showfeatures global showmd5 global showmemory global showtime global spots global uselist global width info = trace_info() quiet = 0 items, argv = getopt.getopt( argv, 'qp:o:F:R:r:w:h:fB:c:e:G:Is:A:DiW:H:S:T:U:XLvPl:y:NO:') for option, value in items: if 0: pass elif option == '-q': quiet = 1 elif option == '-p': password = value elif option == '-o': output = value elif option == '-F': format_ = value elif option == '-R': rotation = float(value) elif option == '-r': resolution = float(output) res_specified = 1 elif option == '-w': width = float(value) elif option == '-h': height = float(value) elif option == '-f': fir = 1 elif option == '-B': band_height = int(value) elif option == '-c': out_cs = parse_colorspace(value) elif option == '-e': proof_filename = value elif option == '-G': gamma_value = float(value) elif option == '-I': invert += 1 elif option == '-W': layout_w = float(value) elif option == '-H': layout_h = float(value) elif option == '-S': layout_em = float(value) elif option == '-U': layout_css = value elif option == '-X': layout_use_doc_css = 0 elif option == '-O': spots = float(value) if not mupdf.FZ_ENABLE_SPOT_RENDERING: jlib.log( 'Spot rendering/Overprint/Overprint simulation not enabled in this build' ) spots = SPOTS_NONE elif option == '-s': if 't' in value: showtime += 1 if 'm' in value: showmemory += 1 if 'f' in value: showfeatures += 1 if '5' in value: showmd5 += 1 elif option == '-A': alphabits_graphics = int(value) sep = value.find('/') if sep >= 0: alphabits_text = int(value[sep + 1:]) else: alphabits_text = alphabits_graphics elif option == '-D': uselist = 0 elif option == '-l': min_line_width = float(value) elif option == '-i': ignore_errors = 1 elif option == '-N': no_icc = 1 elif option == '-T': num_workers = int(value) elif option == '-L': lowmemory = 1 elif option == '-P': bgprint.active = 1 elif option == '-y': layer_config = value elif option == '-v': print(f'mudraw version {mupdf.FZ_VERSION}') if not argv: usage() if num_workers > 0: if uselist == 0: printf('cannot use multiple threads without using display list') sys.exit(1) if band_height == 0: printf('Using multiple threads without banding is pointless') if bgprint.active: if uselist == 0: printf('cannot bgprint without using display list') sys.exit(1) if proof_filename: proof_buffer = mupdf.Buffer(proof_filename) proof_cs = mupdf.Colorspace(FZ_COLORSPACE_NONE, 0, None, proof_buffer) mupdf.set_text_aa_level(alphabits_text) mupdf.set_graphics_aa_level(alphabits_graphics) mupdf.set_graphics_min_line_width(min_line_width) if no_icc: mupdf.disable_icc() else: mupdf.enable_icc() if layout_css: buf = mupdf.Buffer(layout_css) mupdf.set_user_css(buf.string_from_buffer()) mupdf.set_use_document_css(layout_use_doc_css) # Determine output type if band_height < 0: print('Bandheight must be > 0') sys.exit(1) output_format = OUT_PNG if format_: jlib.log('{format=}') for i in range(len(suffix_table)): if format_ == suffix_table[i].suffix[1:]: output_format = suffix_table[i].format if spots == SPOTS_FULL and suffix_table[i].spots == 0: print( f'Output format {suffix_table[i].suffix[1:]} does not support spot rendering.' ) print('Doing overprint simulation instead.') spots = SPOTS_OVERPRINT_SIM break else: print(f'Unknown output format {format}') sys.exit(1) elif output: suffix = output i = 0 while 1: if i == len(suffix_table): break s = suffix.find(suffix_table[i].suffix) if s != -1: suffix = suffix_table[i].suffix[s + 1:] output_format = suffix_table[i].format_ if spots == SPOTS_FULL and suffix_table[i].spots == 0: print( 'Output format {suffix_table[i].suffix[1:]} does not support spot rendering' ) print('Doing overprint simulation instead.') spots = SPOTS_OVERPRINT_SIM i = 0 else: i += 1 if band_height: if output_format not in (OUT_PAM, OUT_PGM, OUT_PPM, OUT_PNM, OUT_PNG, OUT_PBM, OUT_PKM, OUT_PCL, OUT_PCLM, OUT_PS, OUT_PSD): print( 'Banded operation only possible with PxM, PCL, PCLM, PS, PSD, and PNG outputs' ) sys.exit(1) if showmd5: print('Banded operation not compatible with MD5') sys.exit(1) for i in range(len(format_cs_table)): if format_cs_table[i].format_ == output_format: if out_cs == CS_UNSET: out_cs = format_cs_table[i].default_cs for j in range(len(format_cs_table[i].permitted_cs)): if format_cs_table[i].permitted_cs[j] == out_cs: break else: print('Unsupported colorspace for this format') sys.exit(1) alpha = 1 if out_cs in (CS_MONO, CS_GRAY, CS_GRAY_ALPHA): colorspace = mupdf.Colorspace(mupdf.Colorspace.Fixed_GRAY) alpha = (out_cs == CS_GRAY_ALPHA) elif out_cs in (CS_RGB, CS_RGB_ALPHA): colorspace = mupdf.Colorspace(mupdf.Colorspace.Fixed_RGB) alpha = (out_cs == CS_RGB_ALPHA) elif out_cs in (CS_CMYK, CS_CMYK_ALPHA): colorspace = mupdf.Colorspace(mupdf.Colorspace.Fixed_CMYK) alpha = (out_cs == CS_CMYK_ALPHA) elif out_cs == CS_ICC: icc_buffer = mupdf.Buffer(icc_filename) colorspace = Colorspace(mupdf.FZ_COLORSPACE_NONE, 0, None, icc_buffer) alpha = 0 else: print('Unknown colorspace!') sys.exit(1) if out_cs != CS_ICC: colorspace = mupdf.Colorspace(colorspace) else: # Check to make sure this icc profile is ok with the output format */ okay = 0 for i in range(len(format_cs_table)): if format_cs_table[i].format == output_format: for j in range(len(format_cs_table[i].permitted_cs)): x = format_cs_table[i].permitted_cs[j] if x in (CS_MONO, CS_GRAY, CS_GRAY_ALPHA): if colorspace.colorspace_is_gray(): okay = 1 elif x in (CS_RGB, CS_RGB_ALPHA): if colorspace.colorspace_is_rgb(): okay = 1 elif x in (CS_CMYK, CS_CMYK_ALPHA): if colorspace.colorspace_is_cmyk(): okay = 1 if not okay: print( 'ICC profile uses a colorspace that cannot be used for this format' ) sys.exit(1) if output_format == OUT_SVG: # SVG files are always opened for each page. Do not open "output". pass elif output and (not output.startswith('-') or len(output) >= 2) and len(output) >= 1: if has_percent_d(output): output_file_per_page = 1 else: out = mupdf.Output(output, 0) else: quiet = 1 # automatically be quiet if printing to stdout if 0: # Windows specific code to make stdout binary. if output_format not in (OUT_TEXT, OUT_STEXT, OUT_HTML, OUT_XHTML, OUT_TRACE): setmode(fileno(stdout), O_BINARY) out = mupdf.Output(mupdf.Output.Fixed_STDOUT) filename = argv[0] if not output_file_per_page: file_level_headers() timing.count = 0 timing.total = 0 timing.min = 1 << 30 timing.max = 0 timing.mininterp = 1 << 30 timing.maxinterp = 0 timing.minpage = 0 timing.maxpage = 0 timing.minfilename = "" timing.maxfilename = "" if showtime and bgprint.active: timing.total = gettime() fz_optind = 0 while fz_optind < len(argv): filename = argv[fz_optind] fz_optind += 1 files += 1 doc = mupdf.Document(filename) if doc.needs_password(): if not doc.authenticate_password(password): raise Exception(f'cannot authenticate password: {filename}') # Once document is open check for output intent colorspace oi = doc.document_output_intent() if oi: # todo: fz_document_output_intent() can return NULL, and we don't handle this. # See if we had explicitly set a profile to render if out_cs != CS_ICC: # In this case, we want to render to the output intent # color space if the number of channels is the same if oi.colorspace_n() == colorspace.colorspace_n(): colorspace = oi doc.layout_document(layout_w, layout_h, layout_em) if layer_config: apply_layer_config(doc, layer_config) if fz_optind == len(argv) or not mupdf.is_page_range(argv[fz_optind]): drawrange(doc, "1-N") if fz_optind < len(argv) and mupdf.is_page_range(argv[fz_optind]): drawrange(doc, argv[fz_optind]) fz_optind += 1 bgprint_flush() if not output_file_per_page: file_level_trailers() if out: out.close_output() out = None if showtime and timing.count > 0: if bgprint.active: timing.total = gettime() - timing.total if files == 1: print( f'total {timing.total}ms / {timing.count} pages for an average of {timing.total / timing.count}ms' ) if bgprint.active: printf( f'fastest page {timing.minpage}: {timing.mininterp}ms (interpretation) {timing.min - timing.mininterp}ms (rendering) {timing.min}ms(total)' ) print( f'slowest page {timing.maxpage}: {timing.maxinterp}ms (interpretation) {timing.max - timing.maxinterp}ms (rendering) {timing.max}ms(total)' ) else: print(f'fastest page {timing.minpage}: {timing.min}ms') print(f'slowest page {timing.maxpage}: {timing.max}ms') else: print( f'total {timing.total}ms / {timing.count} pages for an average of {timing.total / timing.count}ms in {files} files' ) print( f'fastest page {timing.minpage}: {timing.min}ms ({timing.minfilename})' ) print( f'slowest page {timing.maxpage}: {timing.max}ms ({timing.maxfilename})' ) if showmemory: print( f'Memory use total={info.total} peak={info.peak} current={info.current}' ) return errored != 0