import os import pyembroidery.A10oReader as A10oReader import pyembroidery.A100Reader as A100Reader # import pyembroidery.ArtReader as ArtReader import pyembroidery.BroReader as BroReader import pyembroidery.ColReader as ColReader import pyembroidery.ColWriter as ColWriter import pyembroidery.CsvReader as CsvReader import pyembroidery.CsvWriter as CsvWriter import pyembroidery.DatReader as DatReader import pyembroidery.DsbReader as DsbReader import pyembroidery.DstReader as DstReader import pyembroidery.DstWriter as DstWriter import pyembroidery.DszReader as DszReader import pyembroidery.EdrReader as EdrReader import pyembroidery.EdrWriter as EdrWriter import pyembroidery.EmdReader as EmdReader import pyembroidery.ExpReader as ExpReader import pyembroidery.ExpWriter as ExpWriter import pyembroidery.ExyReader as ExyReader import pyembroidery.FxyReader as FxyReader import pyembroidery.GcodeReader as GcodeReader import pyembroidery.GcodeWriter as GcodeWriter import pyembroidery.GtReader as GtReader import pyembroidery.HusReader as HusReader import pyembroidery.InbReader as InbReader import pyembroidery.InfReader as InfReader import pyembroidery.InfWriter as InfWriter import pyembroidery.JefReader as JefReader import pyembroidery.JefWriter as JefWriter import pyembroidery.JpxReader as JpxReader import pyembroidery.JsonReader as JsonReader import pyembroidery.JsonWriter as JsonWriter import pyembroidery.KsmReader as KsmReader import pyembroidery.MaxReader as MaxReader import pyembroidery.MitReader as MitReader import pyembroidery.NewReader as NewReader import pyembroidery.PcdReader as PcdReader import pyembroidery.PcmReader as PcmReader import pyembroidery.PcqReader as PcqReader import pyembroidery.PcsReader as PcsReader import pyembroidery.PecReader as PecReader import pyembroidery.PecWriter as PecWriter import pyembroidery.PesReader as PesReader import pyembroidery.PesWriter as PesWriter import pyembroidery.PhbReader as PhbReader import pyembroidery.PhcReader as PhcReader import pyembroidery.PmvReader as PmvReader import pyembroidery.PmvWriter as PmvWriter import pyembroidery.PngWriter as PngWriter import pyembroidery.SewReader as SewReader import pyembroidery.ShvReader as ShvReader import pyembroidery.SpxReader as SpxReader import pyembroidery.StcReader as StcReader import pyembroidery.StxReader as StxReader import pyembroidery.SvgWriter as SvgWriter import pyembroidery.TapReader as TapReader import pyembroidery.TbfReader as TbfReader import pyembroidery.TxtWriter as TxtWriter import pyembroidery.U01Reader as U01Reader import pyembroidery.U01Writer as U01Writer import pyembroidery.Vp3Reader as Vp3Reader import pyembroidery.Vp3Writer as Vp3Writer import pyembroidery.XxxReader as XxxReader import pyembroidery.XxxWriter as XxxWriter import pyembroidery.ZhsReader as ZhsReader import pyembroidery.ZxyReader as ZxyReader from .EmbEncoder import Transcoder as Normalizer from .EmbFunctions import * from .EmbThread import EmbThread class EmbPattern: def __init__(self, *args, **kwargs): self.stitches = [] # type: list self.threadlist = [] # type: list self.extras = {} # type: dict # filename, name, category, author, keywords, comments, are typical self._previousX = 0 # type: float self._previousY = 0 # type: float len_args = len(args) if len_args >= 1: arg0 = args[0] if isinstance(arg0, EmbPattern): self.stitches = arg0.stitches[:] self.threadlist = arg0.threadlist[:] self.extras.update(arg0.extras) self._previousX = arg0._previousX self._previousY = arg0._previousY return if len(args) >= 2: settings = args[1] elif "settings" in kwargs: settings = kwargs["settings"] else: settings = kwargs if isinstance(arg0, str): EmbPattern.static_read(arg0, settings=settings, pattern=self) def __ne__(self, other): return not self.__eq__(other) def __eq__(self, other): if not isinstance(other, EmbPattern): return False if self.stitches != other.stitches: return False if self.threadlist != other.threadlist: return False if self.extras != other.extras: return False return True def __str__(self): if "name" in self.extras: return "EmbPattern %s (commands: %3d, threads: %3d)" % ( self.extras["name"], len(self.stitches), len(self.threadlist), ) return "EmbPattern (commands: %3d, threads: %3d)" % ( len(self.stitches), len(self.threadlist), ) def __len__(self): return len(self.stitches) def __getitem__(self, item): if isinstance(item, str): return self.extras[item] return self.stitches[item] def __setitem__(self, key, value): if isinstance(key, str): self.extras[key] = value else: self.stitches[key] = value def __copy__(self): return self.copy() def __deepcopy__(self): return self.copy() def __iadd__(self, other): if isinstance(other, EmbPattern): self.add_pattern(other) elif isinstance(other, EmbThread) or isinstance(other, str): self.add_thread(other) for i in range(0, len(self.stitches)): data = self.stitches[i][2] & COMMAND_MASK if data == STITCH or data == SEW_TO or data == NEEDLE_AT: self.color_change() break # Only add color change if stitching exists. elif isinstance(other, int): self.add_command(other) elif isinstance(other, list) or isinstance(other, tuple): # tuple or list if len(other) == 0: return v = other[0] if isinstance(v, list) or isinstance( v, tuple ): # tuple or list of tuple or lists for v in other: x = v[0] y = v[1] try: cmd = v[2] except IndexError: cmd = STITCH self.add_stitch_absolute(cmd, x, y) elif isinstance(v, complex): # tuple or list of complex for v in other: x = v.real y = v.imag self.add_stitch_absolute(STITCH, x, y) elif isinstance(v, int) or isinstance( v, float ): # tuple or list of numbers. i = 0 ie = len(other) while i < ie: self.add_stitch_absolute(STITCH, other[i], other[i + 1]) i += 2 elif isinstance(v, str): self.extras[v] = other[1] else: raise ValueError() return self def __add__(self, other): p = self.copy() p.add_pattern(other) return p def __radd__(self, other): p = other.copy() p.add_pattern(self) return p def copy(self): emb_pattern = EmbPattern() emb_pattern.stitches = self.stitches[:] emb_pattern.threadlist = self.threadlist[:] emb_pattern.extras.update(self.extras) emb_pattern._previousX = self._previousX emb_pattern._previousY = self._previousY return emb_pattern def clear(self): self.stitches = [] self.threadlist = [] self.extras = {} self._previousX = 0 self._previousY = 0 def read(self, filename, **settings): EmbPattern.static_read(filename, settings=settings, pattern=self) def write(self, filename, **settings): EmbPattern.static_write(self, filename, settings=settings) def move(self, dx=0, dy=0, position=None): """Move dx, dy""" if position is None: self.add_stitch_relative(JUMP, dx, dy) else: self.insert_stitch_relative(position, JUMP, dx, dy) def move_abs(self, x, y, position=None): """Move absolute x, y""" if position is None: self.add_stitch_absolute(JUMP, x, y) else: self.insert(position, JUMP, x, y) def stitch(self, dx=0, dy=0, position=None): """Stitch dx, dy""" if position is None: self.add_stitch_relative(STITCH, dx, dy) else: self.insert_stitch_relative(position, STITCH, dx, dy) def stitch_abs(self, x, y, position=None): """Stitch absolute x, y""" if position is None: self.add_stitch_absolute(STITCH, x, y) else: self.insert(position, STITCH, x, y) def stop(self, dx=0, dy=0, position=None): """Stop dx, dy""" if position is None: self.add_stitch_relative(STOP, dx, dy) else: self.insert_stitch_relative(position, STOP, dx, dy) def trim(self, dx=0, dy=0, position=None): """Trim dx, dy""" if position is None: self.add_stitch_relative(TRIM, dx, dy) else: self.insert_stitch_relative(position, TRIM, dx, dy) def color_change(self, dx=0, dy=0, position=None): """Color Change dx, dy""" if position is None: self.add_stitch_relative(COLOR_CHANGE, dx, dy) else: self.insert_stitch_relative(position, COLOR_CHANGE, dx, dy) def needle_change(self, needle=0, dx=0, dy=0, position=None): """Needle change, needle, dx, dy""" cmd = encode_thread_change(NEEDLE_SET, None, needle) if position is None: self.add_stitch_relative(cmd, dx, dy) else: self.insert_stitch_relative(position, cmd, dx, dy) def sequin_eject(self, dx=0, dy=0, position=None): """Eject Sequin dx, dy""" if position is None: self.add_stitch_relative(SEQUIN_EJECT, dx, dy) else: self.insert_stitch_relative(position, SEQUIN_EJECT, dx, dy) def sequin_mode(self, dx=0, dy=0, position=None): """Eject Sequin dx, dy""" if position is None: self.add_stitch_relative(SEQUIN_MODE, dx, dy) else: self.insert_stitch_relative(position, SEQUIN_MODE, dx, dy) def end(self, dx=0, dy=0, position=None): """End Design dx, dy""" if position is None: self.add_stitch_relative(END, dx, dy) else: self.insert_stitch_relative(position, END, dx, dy) def add_thread(self, thread): """Adds thread to design. Note: this has no effect on stitching and can be done at any point.""" if isinstance(thread, EmbThread): self.threadlist.append(thread) else: thread_object = EmbThread() thread_object.set(thread) self.threadlist.append(thread_object) def metadata(self, name, data): """Adds select metadata to design. Note: this has no effect on stitching and can be done at any point.""" self.extras[name] = data def get_metadata(self, name, default=None): return self.extras.get(name, default) def bounds(self): """Returns the bounds of the stitch data: min_x, min_y, max_x, max_y""" min_x = float("inf") min_y = float("inf") max_x = -float("inf") max_y = -float("inf") for stitch in self.stitches: if stitch[0] > max_x: max_x = stitch[0] if stitch[0] < min_x: min_x = stitch[0] if stitch[1] > max_y: max_y = stitch[1] if stitch[1] < min_y: min_y = stitch[1] return min_x, min_y, max_x, max_y extends = bounds extents = bounds def count_stitch_commands(self, command): count = 0 for stitch in self.stitches: flags = stitch[2] & COMMAND_MASK if flags == command: count += 1 return count def count_color_changes(self): return self.count_stitch_commands(COLOR_CHANGE) def count_needle_sets(self): return self.count_stitch_commands(NEEDLE_SET) def count_stitches(self): return len(self.stitches) def count_threads(self): return len(self.threadlist) @staticmethod def get_random_thread(): thread = EmbThread() thread.set("random") thread.description = "Random" return thread def get_thread_or_filler(self, index): if len(self.threadlist) <= index: return self.get_random_thread() else: return self.threadlist[index] def get_thread(self, index): return self.threadlist[index] def get_match_commands(self, command): for stitch in self.stitches: flags = stitch[2] & COMMAND_MASK if flags == command: yield stitch def get_as_stitchblock(self): stitchblock = [] thread = self.get_thread_or_filler(0) thread_index = 1 for stitch in self.stitches: flags = stitch[2] & COMMAND_MASK if flags == STITCH: stitchblock.append(stitch) else: if len(stitchblock) > 0: yield (stitchblock, thread) stitchblock = [] if flags == COLOR_CHANGE: thread = self.get_thread_or_filler(thread_index) thread_index += 1 if len(stitchblock) > 0: yield (stitchblock, thread) def get_as_command_blocks(self): last_pos = 0 last_command = NO_COMMAND for pos, stitch in enumerate(self.stitches): command = stitch[2] & COMMAND_MASK if command == last_command or last_command == NO_COMMAND: last_command = command continue last_command = command yield self.stitches[last_pos:pos] last_pos = pos yield self.stitches[last_pos:] def get_as_colorblocks(self): """ Returns a generator for colorblocks. Color blocks defined with color_breaks will have the command omitted whereas color blocks delimited with color_change will end with the color_change command, and if delimited with needle_set, the blocks will begin the new color block with the needle_set. """ thread_index = 0 colorblock_start = 0 for pos, stitch in enumerate(self.stitches): command = stitch[2] & COMMAND_MASK if command == COLOR_BREAK: if colorblock_start != pos: thread = self.get_thread_or_filler(thread_index) thread_index += 1 yield self.stitches[colorblock_start:pos], thread colorblock_start = pos + 1 continue if command == COLOR_CHANGE: thread = self.get_thread_or_filler(thread_index) thread_index += 1 yield self.stitches[colorblock_start : pos + 1], thread colorblock_start = pos + 1 continue if command == NEEDLE_SET and colorblock_start != pos: thread = self.get_thread_or_filler(thread_index) thread_index += 1 yield self.stitches[colorblock_start:pos], thread colorblock_start = pos continue if colorblock_start != len(self.stitches): thread = self.get_thread_or_filler(thread_index) yield self.stitches[colorblock_start:], thread def get_as_stitches(self): """pos, x, y, command, v1, v2, v3""" for pos, stitch in enumerate(self.stitches): decode = decode_embroidery_command(stitch[2]) command = decode[0] thread = decode[1] needle = decode[2] order = decode[3] yield pos, stitch[0], stitch[1], command, thread, needle, order def get_unique_threadlist(self): return set(self.threadlist) def get_singleton_threadlist(self): singleton = [] last_thread = None for thread in self.threadlist: if thread != last_thread: singleton.append(thread) last_thread = thread return singleton def move_center_to_origin(self): extends = self.bounds() cx = round((extends[2] - extends[0]) / 2.0) cy = round((extends[3] - extends[1]) / 2.0) self.translate(-cx, -cy) def translate(self, dx, dy): for stitch in self.stitches: stitch[0] += dx stitch[1] += dy def transform(self, matrix): for stitch in self.stitches: matrix.apply(stitch) def fix_color_count(self): """Ensure that there are threads for all color blocks.""" thread_index = 0 init_color = True for stitch in self.stitches: data = stitch[2] & COMMAND_MASK if data == STITCH or data == SEW_TO or data == NEEDLE_AT: if init_color: thread_index += 1 init_color = False elif data == COLOR_CHANGE or data == COLOR_BREAK or data == NEEDLE_SET: init_color = True while len(self.threadlist) < thread_index: self.add_thread(self.get_thread_or_filler(len(self.threadlist))) def add_stitch_absolute(self, cmd, x=0, y=0): """Add a command at the absolute location: x, y""" self.stitches.append([x, y, cmd]) self._previousX = x self._previousY = y def add_stitch_relative(self, cmd, dx=0, dy=0): """Add a command relative to the previous location""" x = self._previousX + dx y = self._previousY + dy self.add_stitch_absolute(cmd, x, y) def insert_stitch_relative(self, position, cmd, dx=0, dy=0): """Insert a relative stitch into the pattern. The stitch is relative to the stitch before it. If inserting at position 0, it's relative to 0,0. If appending, add is called, updating the positioning. """ if position < 0: position += len(self.stitches) # I need positive positions. if position == 0: self.stitches.insert(0, [dx, dy, cmd]) # started (0,0) elif ( position == len(self.stitches) or position is None ): # This is properly just an add. self.add_stitch_relative(cmd, dx, dy) elif 0 < position < len(self.stitches): p = self.stitches[position - 1] x = p[0] + dx y = p[1] + dy self.stitches.insert(position, [x, y, cmd]) def insert(self, position, cmd, x=0, y=0): """Insert a stitch or command""" self.stitches.insert(position, [x, y, cmd]) def prepend_command(self, cmd, x=0, y=0): """Prepend a command, without treating parameters as locations""" self.stitches.insert(0, [x, y, cmd]) def add_command(self, cmd, x=0, y=0): """Add a command, without treating parameters as locations that require an update""" self.stitches.append([x, y, cmd]) def add_block(self, block, thread=None): if thread is not None: self.add_thread(thread) if block is None: return if isinstance(block, list) or isinstance(block, tuple): if len(block) == 0: return v = block[0] if isinstance(v, list) or isinstance(v, tuple): for v in block: x = v[0] y = v[1] try: cmd = v[2] except IndexError: cmd = STITCH self.add_stitch_absolute(cmd, x, y) elif isinstance(v, complex): for v in block: x = v.real y = v.imag self.add_stitch_absolute(STITCH, x, y) elif isinstance(v, int) or isinstance(v, float): i = 0 ie = len(block) while i < ie: self.add_stitch_absolute(STITCH, block[i], block[i + 1]) i += 2 self.add_command(COLOR_BREAK) def add_stitchblock(self, stitchblock): threadlist = self.threadlist block = stitchblock[0] thread = stitchblock[1] if len(threadlist) == 0 or thread is not threadlist[-1]: threadlist.append(thread) self.add_stitch_relative(COLOR_BREAK) else: self.add_stitch_relative(SEQUENCE_BREAK) for stitch in block: try: self.add_stitch_absolute(stitch.command, stitch.x, stitch.y) except AttributeError: self.add_stitch_absolute(stitch[2], stitch[0], stitch[1]) def add_pattern(self, pattern, dx=None, dy=None, sx=None, sy=None, rotate=None): """ add_pattern merges the given pattern with the current pattern. It accounts for some edge conditions but not all of them. If there is an end command on the current pattern, that is removed. If the color ending the current pattern is equal to the color starting the next those color blocks are merged. Any prepended thread change command to the merging pattern is suppressed. :param pattern: pattern to add to current pattern :param dx: position change of the added pattern x :param dy: position change of the added pattern y :param sx: scale of the added pattern x :param sy: scale of the added pattern y :param rotate: rotation of the added pattern :return: """ if isinstance(pattern, str): pattern = EmbPattern(pattern) if self.stitches[-1][2] == END: self.stitches = self.stitches[:-1] # Remove END, if exists if dx is not None or dy is not None: if dx is None: dx = 0 if dy is None: dy = 0 self.add_command(MATRIX_TRANSLATE, dx, dy) if sx is not None or sx is not None: if sx is None: sx = sy if sy is None: sy = sx self.add_command(MATRIX_SCALE, sx, sy) if rotate is not None: self.add_command(MATRIX_ROTATE, rotate) # Add the new thread only if it's different from the last one self.fix_color_count() if len(pattern.threadlist) > 0: if pattern.threadlist[0] == self.threadlist[-1]: self.threadlist.extend(pattern.threadlist[1:]) else: self.threadlist.extend(pattern.threadlist) self.color_change() join_position = len(self.stitches) self.stitches.extend(pattern.stitches) for i in range(join_position, len(self.stitches)): data = self.stitches[i][2] & COMMAND_MASK if data == STITCH or data == SEW_TO or data == NEEDLE_AT: break elif data == COLOR_CHANGE or data == COLOR_BREAK or data == NEEDLE_SET: self.stitches[i][2] = NO_COMMAND self.extras.update(pattern.extras) def interpolate_duplicate_color_as_stop(self): """Processes a pattern replacing any duplicate colors in the threadlist as a stop.""" thread_index = 0 init_color = True last_change = None for position, stitch in enumerate(self.stitches): data = stitch[2] & COMMAND_MASK if data == STITCH or data == SEW_TO or data == NEEDLE_AT: if init_color: try: if ( last_change is not None and thread_index != 0 and self.threadlist[thread_index - 1] == self.threadlist[thread_index] ): del self.threadlist[thread_index] self.stitches[last_change][2] = STOP else: thread_index += 1 except IndexError: # Non-existent threads cannot double return init_color = False elif data == COLOR_CHANGE or data == COLOR_BREAK or data == NEEDLE_SET: init_color = True last_change = position def interpolate_stop_as_duplicate_color(self, thread_change_command=COLOR_CHANGE): """Processes a pattern replacing any stop as a duplicate color, and color_change or another specified thread_change_command""" thread_index = 0 for position, stitch in enumerate(self.stitches): data = stitch[2] & COMMAND_MASK if data == STITCH or data == SEW_TO or data == NEEDLE_AT: continue elif data == COLOR_CHANGE or data == COLOR_BREAK or data == NEEDLE_SET: thread_index += 1 elif data == STOP: try: self.threadlist.insert(thread_index, self.threadlist[thread_index]) self.stitches[position][2] = thread_change_command thread_index += 1 except IndexError: # There are no colors to duplicate return def interpolate_frame_eject(self): """Processes a pattern replacing jump-stop-jump/jump-stop-end sequences with FRAME_EJECT.""" mode = 0 stop_x = None stop_y = None sequence_start_position = None position = 0 ie = len(self.stitches) while position < ie: stitch = self.stitches[position] data = stitch[2] & COMMAND_MASK if ( data == STITCH or data == SEW_TO or data == NEEDLE_AT or data == COLOR_CHANGE or data == COLOR_BREAK or data == NEEDLE_SET ): if mode == 3: del self.stitches[sequence_start_position:position] position = sequence_start_position self.stitches.insert(position, [stop_x, stop_y, FRAME_EJECT]) ie = len(self.stitches) mode = 0 elif data == JUMP: if mode == 2: mode = 3 if mode == 0: sequence_start_position = position mode = 1 elif data == STOP: if mode == 1: mode = 2 stop_x = stitch[0] stop_y = stitch[1] position += 1 if mode >= 2: # Frame_eject at end. del self.stitches[sequence_start_position:position] position = sequence_start_position self.stitches.insert(position, [stop_x, stop_y, FRAME_EJECT]) def interpolate_trims( self, jumps_to_require_trim=None, distance_to_require_trim=None, clipping=True ): """Processes a pattern adding trims according to the given criteria.""" i = -1 ie = len(self.stitches) - 1 x = 0 y = 0 jump_count = 0 jump_start = 0 jump_dx = 0 jump_dy = 0 jumping = False trimmed = True while i < ie: i += 1 stitch = self.stitches[i] dx = stitch[0] - x dy = stitch[1] - y x = stitch[0] y = stitch[1] command = stitch[2] & COMMAND_MASK if command == STITCH or command == SEQUIN_EJECT: trimmed = False jumping = False elif command == COLOR_CHANGE or command == NEEDLE_SET or command == TRIM: trimmed = True jumping = False if command == JUMP: if not jumping: jump_dx = 0 jump_dy = 0 jump_count = 0 jump_start = i jumping = True jump_count += 1 jump_dx += dx jump_dy += dy if not trimmed: if ( jump_count == jumps_to_require_trim or distance_to_require_trim is not None and ( abs(jump_dy) > distance_to_require_trim or abs(jump_dx) > distance_to_require_trim ) ): self.trim(position=jump_start) jump_start += 1 # We inserted a position, start jump has moved. i += 1 ie += 1 trimmed = True if ( clipping and jump_dx == 0 and jump_dy == 0 ): # jump displacement is 0, clip trim command. del self.stitches[jump_start : i + 1] i = jump_start - 1 ie = len(self.stitches) - 1 def get_pattern_interpolate_trim(self, jumps_to_require_trim): """Gets a processed pattern with untrimmed jumps merged and trims added if merged jumps are beyond the given value. The expectation is that it has core commands and not middle-level commands""" new_pattern = EmbPattern() i = -1 ie = len(self.stitches) - 1 count = 0 trimmed = True while i < ie: i += 1 stitch = self.stitches[i] command = stitch[2] & COMMAND_MASK if command == STITCH or command == SEQUIN_EJECT: trimmed = False elif command == COLOR_CHANGE or command == NEEDLE_SET or command == TRIM: trimmed = True if trimmed or stitch[2] != JUMP: new_pattern.add_stitch_absolute(stitch[2], stitch[0], stitch[1]) continue while i < ie and command == JUMP: i += 1 stitch = self.stitches[i] command = stitch[2] count += 1 if command != JUMP: i -= 1 stitch = self.stitches[i] if count >= jumps_to_require_trim: new_pattern.trim() count = 0 new_pattern.add_stitch_absolute(stitch[2], stitch[0], stitch[1]) new_pattern.threadlist.extend(self.threadlist) new_pattern.extras.update(self.extras) return new_pattern def get_pattern_merge_jumps(self): """Returns a pattern with all multiple jumps merged.""" new_pattern = EmbPattern() i = -1 ie = len(self.stitches) - 1 stitch_break = False while i < ie: i += 1 stitch = self.stitches[i] command = stitch[2] & COMMAND_MASK if command == JUMP: if stitch_break: continue new_pattern.add_command(STITCH_BREAK) stitch_break = True continue new_pattern.add_stitch_absolute(stitch[2], stitch[0], stitch[1]) new_pattern.threadlist.extend(self.threadlist) new_pattern.extras.update(self.extras) return new_pattern def get_stable_pattern(self): """Gets a stabilized version of the pattern.""" stable_pattern = EmbPattern() for stitchblock in self.get_as_stitchblock(): stable_pattern.add_stitchblock(stitchblock) stable_pattern.extras.update(self.extras) return stable_pattern def get_normalized_pattern(self, encode_settings=None): """Encodes pattern typically for saving.""" normal_pattern = EmbPattern() transcoder = Normalizer(encode_settings) transcoder.transcode(self, normal_pattern) return normal_pattern def append_translation(self, x, y): """Appends translation to the pattern. All commands will be translated by the given amount, including absolute location commands.""" self.add_stitch_relative(MATRIX_TRANSLATE, x, y) @staticmethod def supported_formats(): """Generates dictionary entries for supported formats. Each entry will always have description, extension, mimetype, and category. Reader will provide the reader, if one exists, writer will provide the writer, if one exists. Metadata gives a list of metadata read and/or written by that type. Options provides accepted options by the format and their accepted values. """ # yield ({ # "description": "Art Embroidery Format", # "extension": "art", # "extensions": ("art",), # "mimetype": "application/x-art", # "category": "embroidery", # "reader": ArtReader, # "metadata": ("name") # }) yield ( { "description": "Brother Embroidery Format", "extension": "pec", "extensions": ("pec",), "mimetype": "application/x-pec", "category": "embroidery", "reader": PecReader, "writer": PecWriter, "metadata": ("name"), } ) yield ( { "description": "Brother Embroidery Format", "extension": "pes", "extensions": ("pes",), "mimetype": "application/x-pes", "category": "embroidery", "reader": PesReader, "writer": PesWriter, "versions": ("1", "6", "1t", "6t"), "metadata": ("name", "author", "category", "keywords", "comments"), } ) yield ( { "description": "Melco Expanded Embroidery Format", "extension": "exp", "extensions": ("exp",), "mimetype": "application/x-exp", "category": "embroidery", "reader": ExpReader, "writer": ExpWriter, } ) # yield ( # { # "description": "Melco Condensed Embroidery Format", # "extension": "cnd", # "extensions": ("cnd",), # "mimetype": "application/x-cnd", # "category": "embroidery", # "reader": CndReader, # } # ) yield ( { "description": "Tajima Embroidery Format", "extension": "dst", "extensions": ("dst",), "mimetype": "application/x-dst", "category": "embroidery", "reader": DstReader, "writer": DstWriter, "read_options": { "trim_distance": (None, 3.0, 50.0), "trim_at": (2, 3, 4, 5, 6, 7, 8), "clipping": (True, False), }, "write_options": {"trim_at": (2, 3, 4, 5, 6, 7, 8)}, "versions": ("default", "extended"), "metadata": ("name", "author", "copyright"), } ) yield ( { "description": "Janome Embroidery Format", "extension": "jef", "extensions": ("jef",), "mimetype": "application/x-jef", "category": "embroidery", "reader": JefReader, "writer": JefWriter, "read_options": { "trim_distance": (None, 3.0, 50.0), "trims": (True, False), "trim_at": (2, 3, 4, 5, 6, 7, 8), "clipping": (True, False), }, "write_options": { "trims": (True, False), "trim_at": (2, 3, 4, 5, 6, 7, 8), }, } ) yield ( { "description": "Pfaff Embroidery Format", "extension": "vp3", "extensions": ("vp3",), "mimetype": "application/x-vp3", "category": "embroidery", "reader": Vp3Reader, "writer": Vp3Writer, } ) yield ( { "description": "Scalable Vector Graphics", "extension": "svg", "extensions": ("svg", "svgz"), "mimetype": "image/svg+xml", "category": "vector", "writer": SvgWriter, } ) yield ( { "description": "Comma-separated values", "extension": "csv", "extensions": ("csv",), "mimetype": "text/csv", "category": "debug", "reader": CsvReader, "writer": CsvWriter, "versions": ("default", "delta", "full"), } ) yield ( { "description": "Singer Embroidery Format", "extension": "xxx", "extensions": ("xxx",), "mimetype": "application/x-xxx", "category": "embroidery", "reader": XxxReader, "writer": XxxWriter, } ) yield ( { "description": "Janome Embroidery Format", "extension": "sew", "extensions": ("sew",), "mimetype": "application/x-sew", "category": "embroidery", "reader": SewReader, } ) yield ( { "description": "Barudan Embroidery Format", "extension": "u01", "extensions": ("u00", "u01", "u02"), "mimetype": "application/x-u01", "category": "embroidery", "reader": U01Reader, "writer": U01Writer, } ) yield ( { "description": "Husqvarna Viking Embroidery Format", "extension": "shv", "extensions": ("shv",), "mimetype": "application/x-shv", "category": "embroidery", "reader": ShvReader, } ) yield ( { "description": "Toyota Embroidery Format", "extension": "10o", "extensions": ("10o",), "mimetype": "application/x-10o", "category": "embroidery", "reader": A10oReader, } ) yield ( { "description": "Toyota Embroidery Format", "extension": "100", "extensions": ("100",), "mimetype": "application/x-100", "category": "embroidery", "reader": A100Reader, } ) yield ( { "description": "Bits & Volts Embroidery Format", "extension": "bro", "extensions": ("bro",), "mimetype": "application/x-Bro", "category": "embroidery", "reader": BroReader, } ) yield ( { "description": "Sunstar or Barudan Embroidery Format", "extension": "dat", "extensions": ("dat",), "mimetype": "application/x-dat", "category": "embroidery", "reader": DatReader, } ) yield ( { "description": "Tajima(Barudan) Embroidery Format", "extension": "dsb", "extensions": ("dsb",), "mimetype": "application/x-dsb", "category": "embroidery", "reader": DsbReader, } ) yield ( { "description": "ZSK USA Embroidery Format", "extension": "dsz", "extensions": ("dsz",), "mimetype": "application/x-dsz", "category": "embroidery", "reader": DszReader, } ) yield ( { "description": "Elna Embroidery Format", "extension": "emd", "extensions": ("emd",), "mimetype": "application/x-emd", "category": "embroidery", "reader": EmdReader, } ) yield ( { "description": "Eltac Embroidery Format", "extension": "exy", # e??, e01 "extensions": ("e00", "e01", "e02"), "mimetype": "application/x-exy", "category": "embroidery", "reader": ExyReader, } ) yield ( { "description": "Fortron Embroidery Format", "extension": "fxy", # f??, f01 "extensions": ("f00", "f01", "f02"), "mimetype": "application/x-fxy", "category": "embroidery", "reader": FxyReader, } ) yield ( { "description": "Gold Thread Embroidery Format", "extension": "gt", "extensions": ("gt",), "mimetype": "application/x-exy", "category": "embroidery", "reader": GtReader, } ) yield ( { "description": "Inbro Embroidery Format", "extension": "inb", "extensions": ("inb",), "mimetype": "application/x-inb", "category": "embroidery", "reader": InbReader, } ) yield ( { "description": "Tajima Embroidery Format", "extension": "tbf", "extensions": ("tbf",), "mimetype": "application/x-tbf", "category": "embroidery", "reader": TbfReader, } ) yield ( { "description": "Pfaff Embroidery Format", "extension": "ksm", "extensions": ("ksm",), "mimetype": "application/x-ksm", "category": "embroidery", "reader": KsmReader, } ) yield ( { "description": "Happy Embroidery Format", "extension": "tap", "extensions": ("tap",), "mimetype": "application/x-tap", "category": "embroidery", "reader": TapReader, } ) yield ( { "description": "Pfaff Embroidery Format", "extension": "spx", "extensions": ("spx"), "mimetype": "application/x-spx", "category": "embroidery", "reader": SpxReader, } ) yield ( { "description": "Data Stitch Embroidery Format", "extension": "stx", "extensions": ("stx",), "mimetype": "application/x-stx", "category": "embroidery", "reader": StxReader, } ) yield ( { "description": "Brother Embroidery Format", "extension": "phb", "extensions": ("phb",), "mimetype": "application/x-phb", "category": "embroidery", "reader": PhbReader, } ) yield ( { "description": "Brother Embroidery Format", "extension": "phc", "extensions": ("phc",), "mimetype": "application/x-phc", "category": "embroidery", "reader": PhcReader, } ) yield ( { "description": "Ameco Embroidery Format", "extension": "new", "extensions": ("new",), "mimetype": "application/x-new", "category": "embroidery", "reader": NewReader, } ) yield ( { "description": "Pfaff Embroidery Format", "extension": "max", "extensions": ("max",), "mimetype": "application/x-max", "category": "embroidery", "reader": MaxReader, } ) yield ( { "description": "Mitsubishi Embroidery Format", "extension": "mit", "extensions": ("mit",), "mimetype": "application/x-mit", "category": "embroidery", "reader": MitReader, } ) yield ( { "description": "Pfaff Embroidery Format", "extension": "pcd", "extensions": ("pcd",), "mimetype": "application/x-pcd", "category": "embroidery", "reader": PcdReader, } ) yield ( { "description": "Pfaff Embroidery Format", "extension": "pcq", "extensions": ("pcq",), "mimetype": "application/x-pcq", "category": "embroidery", "reader": PcqReader, } ) yield ( { "description": "Pfaff Embroidery Format", "extension": "pcm", "extensions": ("pcm",), "mimetype": "application/x-pcm", "category": "embroidery", "reader": PcmReader, } ) yield ( { "description": "Pfaff Embroidery Format", "extension": "pcs", "extensions": ("pcs",), "mimetype": "application/x-pcs", "category": "embroidery", "reader": PcsReader, } ) yield ( { "description": "Janome Embroidery Format", "extension": "jpx", "extensions": ("jpx",), "mimetype": "application/x-jpx", "category": "embroidery", "reader": JpxReader, } ) yield ( { "description": "Gunold Embroidery Format", "extension": "stc", "extensions": ("stc",), "mimetype": "application/x-stc", "category": "embroidery", "reader": StcReader, } ) yield ({ "description": "Zeng Hsing Embroidery Format", "extension": "zhs", "extensions": ("zhs",), "mimetype": "application/x-zhs", "category": "embroidery", "reader": ZhsReader }) yield ( { "description": "ZSK TC Embroidery Format", "extension": "zxy", "extensions": ("z00", "z01", "z02"), "mimetype": "application/x-zxy", "category": "embroidery", "reader": ZxyReader, } ) yield ( { "description": "Brother Stitch Format", "extension": "pmv", "extensions": ("pmv",), "mimetype": "application/x-pmv", "category": "stitch", "reader": PmvReader, "writer": PmvWriter, } ) yield ( { "description": "PNG Format, Portable Network Graphics", "extension": "png", "extensions": ("png",), "mimetype": "image/png", "category": "image", "writer": PngWriter, "write_options": { "background": (0x000000, 0xFFFFFF), "linewidth": (1, 2, 3, 4, 5, 6, 7, 8, 9, 10), }, } ) yield ( { "description": "txt Format, Text File", "extension": "txt", "extensions": ("txt",), "mimetype": "text/plain", "category": "debug", "writer": TxtWriter, "versions": ("default", "embroidermodder"), } ) yield ( { "description": "gcode Format, Text File", "extension": "gcode", "extensions": ("gcode", "g-code", "ngc", "nc", ".g"), "mimetype": "text/plain", "category": "embroidery", "reader": GcodeReader, "writer": GcodeWriter, "write_options": { "stitch_z_travel": (5.0, 10.0), }, } ) yield ( { "description": "Husqvarna Embroidery Format", "extension": "hus", "extensions": ("hus",), "mimetype": "application/x-hus", "category": "embroidery", "reader": HusReader, } ) yield ( { "description": "Edr Color Format", "extension": "edr", "extensions": ("edr",), "mimetype": "application/x-edr", "category": "color", "reader": EdrReader, "writer": EdrWriter, } ) yield ( { "description": "Col Color Format", "extension": "col", "extensions": ("col",), "mimetype": "application/x-col", "category": "color", "reader": ColReader, "writer": ColWriter, } ) yield ( { "description": "Inf Color Format", "extension": "inf", "extensions": ("inf",), "mimetype": "application/x-inf", "category": "color", "reader": InfReader, "writer": InfWriter, } ) yield ( { "description": "Json Export", "extension": "json", "extensions": ("json",), "mimetype": "application/json", "category": "debug", "reader": JsonReader, "writer": JsonWriter, } ) @staticmethod def convert(filename_from, filename_to, settings=None): pattern = EmbPattern.static_read(filename_from, settings) if pattern is None: return EmbPattern.static_write(pattern, filename_to, settings) @staticmethod def get_extension_by_filename(filename): """extracts the extension from a filename""" return os.path.splitext(filename)[1][1:] @staticmethod def read_embroidery(reader, f, settings=None, pattern=None): """Reads fileobject or filename with reader.""" if reader is None: return None if pattern is None: pattern = EmbPattern() if EmbPattern.is_str(f): text_mode = False try: text_mode = reader.READ_FILE_IN_TEXT_MODE except AttributeError: pass if text_mode: try: with open(f, "r") as stream: reader.read(stream, pattern, settings) stream.close() except IOError: pass else: try: with open(f, "rb") as stream: reader.read(stream, pattern, settings) stream.close() except IOError: pass else: reader.read(f, pattern, settings) return pattern @staticmethod def read_dst(f, settings=None, pattern=None): """Reads fileobject as DST file""" return EmbPattern.read_embroidery(DstReader, f, settings, pattern) @staticmethod def read_pec(f, settings=None, pattern=None): """Reads fileobject as PEC file""" return EmbPattern.read_embroidery(PecReader, f, settings, pattern) @staticmethod def read_pes(f, settings=None, pattern=None): """Reads fileobject as PES file""" return EmbPattern.read_embroidery(PesReader, f, settings, pattern) @staticmethod def read_exp(f, settings=None, pattern=None): """Reads fileobject as EXP file""" return EmbPattern.read_embroidery(ExpReader, f, settings, pattern) @staticmethod def read_vp3(f, settings=None, pattern=None): """Reads fileobject as VP3 file""" return EmbPattern.read_embroidery(Vp3Reader, f, settings, pattern) @staticmethod def read_jef(f, settings=None, pattern=None): """Reads fileobject as JEF file""" return EmbPattern.read_embroidery(JefReader, f, settings, pattern) @staticmethod def read_u01(f, settings=None, pattern=None): """Reads fileobject as U01 file""" return EmbPattern.read_embroidery(U01Reader, f, settings, pattern) @staticmethod def read_csv(f, settings=None, pattern=None): """Reads fileobject as CSV file""" return EmbPattern.read_embroidery(CsvReader, f, settings, pattern) @staticmethod def read_gcode(f, settings=None, pattern=None): """Reads fileobject as GCode file""" return EmbPattern.read_embroidery(GcodeReader, f, settings, pattern) @staticmethod def read_xxx(f, settings=None, pattern=None): """Reads fileobject as XXX file""" return EmbPattern.read_embroidery(XxxReader, f, settings, pattern) @staticmethod def static_read(filename, settings=None, pattern=None): """Reads file, assuming type by extension""" extension = EmbPattern.get_extension_by_filename(filename) extension = extension.lower() for file_type in EmbPattern.supported_formats(): if file_type["extension"] != extension: continue reader = file_type.get("reader", None) return EmbPattern.read_embroidery(reader, filename, settings, pattern) return None @staticmethod def write_embroidery(writer, pattern, stream, settings=None): if pattern is None: return if settings is None: settings = {} else: settings = settings.copy() try: encode = writer.ENCODE except AttributeError: encode = True if settings.get("encode", encode): if not ("max_jump" in settings): try: settings["max_jump"] = writer.MAX_JUMP_DISTANCE except AttributeError: pass if not ("max_stitch" in settings): try: settings["max_stitch"] = writer.MAX_STITCH_DISTANCE except AttributeError: pass if not ("full_jump" in settings): try: settings["full_jump"] = writer.FULL_JUMP except AttributeError: pass if not ("round" in settings): try: settings["round"] = writer.ROUND except AttributeError: pass if not ("writes_speeds" in settings): try: settings["writes_speeds"] = writer.WRITES_SPEEDS except AttributeError: pass if not ("sequin_contingency" in settings): try: settings["sequin_contingency"] = writer.SEQUIN_CONTINGENCY except AttributeError: pass if not ("thread_change_command" in settings): try: settings["thread_change_command"] = writer.THREAD_CHANGE_COMMAND except AttributeError: pass if not ("translate" in settings): try: settings["translate"] = writer.TRANSLATE except AttributeError: pass if not ("scale" in settings): try: settings["scale"] = writer.SCALE except AttributeError: pass if not ("rotate" in settings): try: settings["rotate"] = writer.ROTATE except AttributeError: pass pattern = pattern.get_normalized_pattern(settings) if EmbPattern.is_str(stream): text_mode = False try: text_mode = writer.WRITE_FILE_IN_TEXT_MODE except AttributeError: pass if text_mode: try: with open(stream, "w") as stream: writer.write(pattern, stream, settings) except IOError: pass else: try: with open(stream, "wb") as stream: writer.write(pattern, stream, settings) except IOError: pass else: writer.write(pattern, stream, settings) @staticmethod def write_dst(pattern, stream, settings=None): """Writes fileobject as DST file""" EmbPattern.write_embroidery(DstWriter, pattern, stream, settings) @staticmethod def write_pec(pattern, stream, settings=None): """Writes fileobject as PEC file""" EmbPattern.write_embroidery(PecWriter, pattern, stream, settings) @staticmethod def write_pes(pattern, stream, settings=None): """Writes fileobject as PES file""" EmbPattern.write_embroidery(PesWriter, pattern, stream, settings) @staticmethod def write_exp(pattern, stream, settings=None): """Writes fileobject as EXP file""" EmbPattern.write_embroidery(ExpWriter, pattern, stream, settings) @staticmethod def write_vp3(pattern, stream, settings=None): """Writes fileobject as Vp3 file""" EmbPattern.write_embroidery(Vp3Writer, pattern, stream, settings) @staticmethod def write_jef(pattern, stream, settings=None): """Writes fileobject as JEF file""" EmbPattern.write_embroidery(JefWriter, pattern, stream, settings) @staticmethod def write_u01(pattern, stream, settings=None): """Writes fileobject as U01 file""" EmbPattern.write_embroidery(U01Writer, pattern, stream, settings) @staticmethod def write_csv(pattern, stream, settings=None): """Writes fileobject as CSV file""" EmbPattern.write_embroidery(CsvWriter, pattern, stream, settings) @staticmethod def write_txt(pattern, stream, settings=None): """Writes fileobject as CSV file""" EmbPattern.write_embroidery(TxtWriter, pattern, stream, settings) @staticmethod def write_gcode(pattern, stream, settings=None): """Writes fileobject as Gcode file""" EmbPattern.write_embroidery(GcodeWriter, pattern, stream, settings) @staticmethod def write_xxx(pattern, stream, settings=None): """Writes fileobject as XXX file""" EmbPattern.write_embroidery(XxxWriter, pattern, stream, settings) @staticmethod def write_svg(pattern, stream, settings=None): """Writes fileobject as DST file""" EmbPattern.write_embroidery(SvgWriter, pattern, stream, settings) @staticmethod def write_png(pattern, stream, settings=None): """Writes fileobject as PNG file""" EmbPattern.write_embroidery(PngWriter, pattern, stream, settings) @staticmethod def static_write(pattern, filename, settings=None): """Writes file, assuming type by extension""" extension = EmbPattern.get_extension_by_filename(filename) extension = extension.lower() for file_type in EmbPattern.supported_formats(): if file_type["extension"] != extension: continue writer = file_type.get("writer", None) if writer is None: continue EmbPattern.write_embroidery(writer, pattern, filename, settings) @staticmethod def is_str(obj): try: return isinstance(obj, basestring) except NameError: return isinstance(obj, str)