def draw_alpha_frames_merged(self): for idx, bmode in enumerate(self.blending_modes): max_w = 0 max_h = 0 blendomatic_frames = [] for tile in bmode["alphamasks"]: frame = PNG(0, None, tile["data"]) tw = tile["width"] th = tile["height"] frame.create(tw, th, True) if tw > max_w: max_w = tw if th > max_h: max_h = th blendomatic_frames.append((frame, None, (0, 0))) atlas, atlas_meta, (width, height) = merge_frames(blendomatic_frames, max_w, max_h) meta_out = generate_meta_text(atlas_meta) yield idx, atlas, (width, height), meta_out
def draw_frames_merged(self, color_table): # generate all frames, them merge them on one big texture # player-specific colors will be in color blue, but with an alpha of 254 player_id = 1 max_width = 0 max_height = 0 slp_pngs = [] for frame in self.frames: png = PNG(player_id, color_table, frame.get_picture_data()) png.create() if png.width > max_width: max_width = png.width if png.height > max_height: max_height = png.height slp_pngs.append((png, frame.info.size, frame.info.hotspot)) # now we collected all sprites and can start merging them to one # big texture atlas. atlas, atlas_meta, (width, height) = merge_frames(slp_pngs, max_width, max_height) meta_out = generate_meta_text(atlas_meta) return atlas, (width, height), meta_out
def draw_bit_frames(self): for idx, bmode in enumerate(self.blending_modes): for tidx, tile in enumerate(bmode["bitmasks"]): png = PNG(0, None, tile["data"]) png.create(tile["width"], tile["height"], True) yield png, idx, tidx
def main(): parsed = init() # In insert mode with parsed.carrier as image: data = image.read() original_image = PNG(data, verbose=VERBOSE) if parsed.secret: modified_image = insert(original_image, parsed.secret) output_data = modified_image.export_image() else: # in extract mode output_data = extract(original_image) print('Secret message: "{}"'.format(output_data.decode(ENCODING))) # After finished parsing, output file with parsed.output_file as output_file: output_file.write(output_data) # Print file statistics file_metadata("Original file information:", parsed.carrier.name, data) file_metadata("Exported file information:", parsed.output_file.name, output_data)
def write_glyphs(self, glyphs, glyph_filenames, image_format): write_func = self.image_write_func(image_format) for glyph in glyphs: img_file = glyph_filenames[glyph] offset = self.tell() write_func(PNG(img_file)) self.glyph_maps.append(GlyphMap(glyph, offset, image_format))
def write_glyphs(self, glyphs, glyph_filenames, image_format): write_func = self.image_write_func(image_format) for glyph in glyphs: img_file = glyph_filenames[glyph] # print 'writing data for glyph %s' % path.basename(img_file) offset = self.tell() write_func(PNG(img_file)) self.glyph_maps.append(GlyphMap(glyph, offset, image_format))
def draw_frames(self, color_table): # player-specific colors will be in color blue, but with an alpha of 254 player_id = 1 for idx, frame in enumerate(self.frames): png = PNG(player_id, color_table, frame.get_picture_data()) png.create() # this sprite png only contains one texture, therefore x and y are 0 drawn_meta = { "tx": 0, "ty": 0, "tw": frame.info.size[0], "th": frame.info.size[1], "hx": frame.info.hotspot[0], "hy": frame.info.hotspot[1], } meta_out = generate_meta_text([drawn_meta]) yield png, meta_out
def save(self, filename): # Initialize an array to hold the colors converted # to 32 bit unsigned integers intData = [] for x in range(self.width): intData.append([]) for y in range(self.height): argb = self.data[x][y] # Reorder the alpha to the back intData[-1].append(CTI(argb[1], argb[2], argb[3], argb[0])) # Actually save the information to a file PNG(intData, filename)
def saveDepth(self, filename): minDepth = None maxDepth = None # Find the range of values in the depth array for x in range(self.width): for y in range(self.height): depthVal = self.depth[x][y] if depthVal is not None: if maxDepth is None or depthVal > maxDepth: maxDepth = depthVal if minDepth is None or depthVal < minDepth: minDepth = depthVal # Define a method for reverse linear (inter/extra)polation. def RLerp(v0, v1, val): return float(val - v0) / float(v1 - v0) # Create visual for depth intData = [] # First make sure min/max are different and not None if minDepth is None or maxDepth is None or minDepth == maxDepth: # There is no real depth information, white screen for x in range(self.width): intData.append([]) for y in range(self.height): intData[-1].append(0x00000000) else: # Depth information exists and makes sense for x in range(self.width): intData.append([]) for y in range(self.height): depthVal = self.depth[x][y] if depthVal is not None: depthScale = RLerp(minDepth, maxDepth, depthVal) depthColor = int(depthScale * 255) intData[-1].append( CTI(depthColor, depthColor, depthColor, 255)) else: intData[-1].append(CTI(0, 0, 0, 0)) PNG(intData, filename)
img_pairs.sort(key=lambda pair: (len(pair[0]), pair[0]), reverse=True) # Add dummy GlyphOrder for code in [ 0x23, 0x2a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x20e3, 0x200d, 0x1f9b0, 0x1f9b1, 0x1f9b2, 0x1f9b3, 0xfe0f, 0xe0062, 0xe0063, 0xe0065, 0xe0067, 0xe006c, 0xe006e, 0xe0073, 0xe0074, 0xe0077, 0xe007f ]: g.append("%04X" % code) for (u, filename) in img_pairs: codes = u.split("-") n = glyph_name(codes) print "Adding glyph for %s" % n g.append(n) for code in codes: code = int(code, 16) if code not in c: name = glyph_name(["%04X" % code]) c[code] = name if len(codes) > 1: h[name] = [0, 0] (img_width, img_height) = PNG(filename).get_size() advance = int(round((float(ascent + descent) * img_width / img_height))) h[n] = [advance, 0] if len(codes) > 1: add_ligature(font, codes) font.saveXML(out_file)
def map_fn(filename): wid, ht = PNG(filename).get_size() return int(round(float(lineheight) * wid / ht))
def setUp(self): png_path = os.path.join(fixtures_dir, 'gogopher.png') self.png = PNG(open(png_path))
def main(argv): import glob from fontTools import ttx, ttLib options = [] option_map = { "-V": "verbose", "-O": "keep_outlines", "-U": "uncompressed", "-C": "keep_chunks", } for key, value in option_map.items(): if key in argv: options.append(value) argv.remove(key) if len(argv) < 4: print >> sys.stderr, """ Usage: emoji_builder.py [-V] [-O] [-U] [-A] font.ttf out-font.ttf strike-prefix... This will search for files that have strike-prefix followed by a hex number, and end in ".png". For example, if strike-prefix is "icons/uni", then files with names like "icons/uni1f4A9.png" will be loaded. All images for the same strike should have the same size for best results. If multiple strike-prefix parameters are provided, multiple strikes will be embedded, in the order provided. The script then embeds color bitmaps in the font, for characters that the font already supports, and writes the new font out. If -V is given, verbose mode is enabled. If -U is given, uncompressed images are stored (imageFormat=1). By default, PNG images are stored (imageFormat=17). If -O is given, the outline tables ('glyf', 'CFF ') and related tables are NOT dropped from the font. By default they are dropped. If -C is given, unused chunks (color profile, etc) are NOT dropped from the PNG images when embedding. By default they are dropped. """ sys.exit(1) font_file = argv[1] out_file = argv[2] img_prefixes = argv[3:] del argv def add_font_table(font, tag, data): tab = ttLib.tables.DefaultTable.DefaultTable(tag) tab.data = str(data) font[tag] = tab def drop_outline_tables(font): for tag in ['cvt ', 'fpgm', 'glyf', 'loca', 'prep', 'CFF ', 'VORG']: try: del font[tag] except KeyError: pass print font = ttx.TTFont(font_file) print "Loaded font '%s'." % font_file font_metrics = FontMetrics(font['head'].unitsPerEm, font['hhea'].ascent, -font['hhea'].descent) print "Font metrics: upem=%d ascent=%d descent=%d." % \ (font_metrics.upem, font_metrics.ascent, font_metrics.descent) glyph_metrics = font['hmtx'].metrics unicode_cmap = font['cmap'].getcmap(3, 10) if not unicode_cmap: unicode_cmap = font['cmap'].getcmap(3, 1) if not unicode_cmap: raise Exception("Failed to find a Unicode cmap.") image_format = 1 if 'uncompressed' in options else 17 ebdt = CBDT(font_metrics, options) ebdt.write_header() eblc = CBLC(font_metrics, options) eblc.write_header() eblc.start_strikes(len(img_prefixes)) for img_prefix in img_prefixes: print img_files = {} glb = "%s*.png" % img_prefix print "Looking for images matching '%s'." % glb pattern = re.compile("^[A-Fa-f0-9]+$") for img_file in glob.glob(glb): if not pattern.match(img_file[len(img_prefix):-4]): continue uchar = int(img_file[len(img_prefix):-4], 16) img_files[uchar] = img_file if not img_files: raise Exception("No image files found in '%s'." % glb) print "Found images for %d characters in '%s'." % (len(img_files), glb) glyph_imgs = {} advance = width = height = 0 for uchar, img_file in img_files.items(): if uchar in unicode_cmap.cmap: glyph_name = unicode_cmap.cmap[uchar] glyph_id = font.getGlyphID(glyph_name) glyph_imgs[glyph_id] = img_file if "verbose" in options: print "Matched U+%04X: id=%d name=%s image=%s" % (uchar, glyph_id, glyph_name, img_file) advance += glyph_metrics[glyph_name][0] w, h = PNG(img_file).get_size() width += w height += h glyphs = sorted(glyph_imgs.keys()) if not glyphs: raise Exception("No common characteres found between font and '%s'." % glb) print "Embedding images for %d glyphs for this strike." % len(glyphs) advance, width, height = (div(x, len(glyphs)) for x in (advance, width, height)) strike_metrics = StrikeMetrics(font_metrics, advance, width, height) print "Strike ppem set to %d." % (strike_metrics.y_ppem) ebdt.start_strike(strike_metrics) ebdt.write_glyphs(glyphs, glyph_imgs, image_format) glyph_maps = ebdt.end_strike() eblc.write_strike(strike_metrics, glyph_maps) print ebdt = ebdt.data() add_font_table(font, 'CBDT', ebdt) print "CBDT table synthesized: %d bytes." % len(ebdt) eblc.end_strikes() eblc = eblc.data() add_font_table(font, 'CBLC', eblc) print "CBLC table synthesized: %d bytes." % len(eblc) print if 'keep_outlines' not in options: drop_outline_tables(font) print "Dropped outline ('glyf', 'CFF ') and related tables." font.save(out_file) print "Output font '%s' generated." % out_file
def main(): png = PNG("sample.png") png.load_image()
def main(argv): import glob from fontTools import ttx, ttLib options = [] option_map = { "-V": "verbose", "-O": "keep_outlines", "-U": "uncompressed", "-S": "small_glyph_metrics", "-C": "keep_chunks", } for key, value in option_map.items(): if key in argv: options.append(value) argv.remove(key) if len(argv) < 4: print(""" Usage: emoji_builder.py [-V] [-O] [-U] [-S] [-A] font.ttf out-font.ttf strike-prefix... This will search for files that have strike-prefix followed by a hex number, and end in ".png". For example, if strike-prefix is "icons/uni", then files with names like "icons/uni1f4A9.png" will be loaded. All images for the same strike should have the same size for best results. If multiple strike-prefix parameters are provided, multiple strikes will be embedded, in the order provided. The script then embeds color bitmaps in the font, for characters that the font already supports, and writes the new font out. If -V is given, verbose mode is enabled. If -U is given, uncompressed images are stored (imageFormat=1). If -S is given, PNG images are stored with small glyph metrics (imageFormat=17). By default, PNG images are stored with big glyph metrics (imageFormat=18). If -O is given, the outline tables ('glyf', 'CFF ') and related tables are NOT dropped from the font. By default they are dropped. If -C is given, unused chunks (color profile, etc) are NOT dropped from the PNG images when embedding. By default they are dropped. """, file=sys.stderr) sys.exit(1) font_file = argv[1] out_file = argv[2] img_prefixes = argv[3:] del argv def add_font_table(font, tag, data): tab = ttLib.tables.DefaultTable.DefaultTable(tag) tab.data = str(data) font[tag] = tab def drop_outline_tables(font): for tag in ['cvt ', 'fpgm', 'glyf', 'loca', 'prep', 'CFF ', 'VORG']: try: del font[tag] except KeyError: pass print() font = ttx.TTFont(font_file) print("Loaded font '%s'." % font_file) font_metrics = FontMetrics(font['head'].unitsPerEm, font['hhea'].ascent, -font['hhea'].descent) print("Font metrics: upem=%d ascent=%d descent=%d." % \ (font_metrics.upem, font_metrics.ascent, font_metrics.descent)) glyph_metrics = font['hmtx'].metrics unicode_cmap = font['cmap'].getcmap(3, 10) if not unicode_cmap: unicode_cmap = font['cmap'].getcmap(3, 1) if not unicode_cmap: raise Exception("Failed to find a Unicode cmap.") image_format = 1 if 'uncompressed' in options else ( 17 if 'small_glyph_metrics' in options else 18) ebdt = CBDT(font_metrics, options) ebdt.write_header() eblc = CBLC(font_metrics, options) eblc.write_header() eblc.start_strikes(len(img_prefixes)) def is_vs(cp): return cp >= 0xfe00 and cp <= 0xfe0f for img_prefix in img_prefixes: print() img_files = {} glb = "%s*.png" % img_prefix print("Looking for images matching '%s'." % glb) for img_file in glob.glob(glb): codes = img_file[len(img_prefix):-4] if "_" in codes: pieces = codes.split("_") cps = [int(code, 16) for code in pieces] uchars = "".join([unichr(cp) for cp in cps if not is_vs(cp)]) else: cp = int(codes, 16) if is_vs(cp): print("ignoring unexpected vs input %04x" % cp) continue uchars = unichr(cp) img_files[uchars] = img_file if not img_files: raise Exception("No image files found in '%s'." % glb) print("Found images for %d characters in '%s'." % (len(img_files), glb)) glyph_imgs = {} advance = width = height = 0 for uchars, img_file in img_files.items(): if len(uchars) == 1: try: glyph_name = unicode_cmap.cmap[ord(uchars)] except: print("no cmap entry for %x" % ord(uchars)) raise ValueError("%x" % ord(uchars)) else: glyph_name = get_glyph_name_from_gsub(uchars, font, unicode_cmap.cmap) glyph_id = font.getGlyphID(glyph_name) glyph_imgs[glyph_id] = img_file if "verbose" in options: uchars_name = ",".join(["%04X" % ord(char) for char in uchars]) # print "Matched U+%s: id=%d name=%s image=%s" % ( # uchars_name, glyph_id, glyph_name, img_file) advance += glyph_metrics[glyph_name][0] w, h = PNG(img_file).get_size() width += w height += h glyphs = sorted(glyph_imgs.keys()) if not glyphs: raise Exception( "No common characters found between font and '%s'." % glb) print("Embedding images for %d glyphs for this strike." % len(glyphs)) advance, width, height = (div(x, len(glyphs)) for x in (advance, width, height)) strike_metrics = StrikeMetrics(font_metrics, advance, width, height) print("Strike ppem set to %d." % (strike_metrics.y_ppem)) ebdt.start_strike(strike_metrics) ebdt.write_glyphs(glyphs, glyph_imgs, image_format) glyph_maps = ebdt.end_strike() eblc.write_strike(strike_metrics, glyph_maps) print() ebdt = ebdt.data() add_font_table(font, 'CBDT', ebdt) print("CBDT table synthesized: %d bytes." % len(ebdt)) eblc.end_strikes() eblc = eblc.data() add_font_table(font, 'CBLC', eblc) print("CBLC table synthesized: %d bytes." % len(eblc)) print() if 'keep_outlines' not in options: drop_outline_tables(font) print("Dropped outline ('glyf', 'CFF ') and related tables.") # hack removal of cmap pua entry for unknown flag glyph. If we try to # remove it earlier, getGlyphID dies. Need to restructure all of this # code. font_data.delete_from_cmap(font, [0xfe82b]) font.save(out_file) print("Output font '%s' generated." % out_file)
#!/usr/bin/env python3 from png import PNG, chunks, filter WIDTH = HEIGHT = 50 data = WIDTH * HEIGHT * bytes([255, 0, 0]) p = PNG() p.chunks.append(chunks.IHDR(width=WIDTH, height=HEIGHT)) p.chunks.append(chunks.IDAT(uncompressed_data=filter.encode(data, WIDTH*3))) p.chunks.append(chunks.IEND()) with open("/tmp/test.png", "wb") as f: f.write(p.dump())
#!/usr/bin/env python3 PROG_NAME = "PNG Chunk Dump" from png import PNG import argparse import logging p = argparse.ArgumentParser(prog=PROG_NAME, description="Analyzes and edits PNG Chunks") p.add_argument("-i", "--ignore-errors", action="store_true", help="Ignore Length, Name and CRC errors") p.add_argument("-v", "--verbose", action="store_const", default=logging.INFO, const=logging.DEBUG, help="be more verbose") p.add_argument("file", metavar="FILE", type=argparse.FileType("rb"), help="Input file") args = p.parse_args() logging.basicConfig(format="[%(asctime)s] %(levelname)s: %(message)s", level=args.verbose) png = PNG.load(args.file.read(), ignore_errors=args.ignore_errors) print(png) for chunk in png.chunks: print(">", chunk)