Download raw (32.1 KB)
import re import json import chiplotle from chiplotle.tools.plottertools \ import instantiate_virtual_plotter from chiplotle.geometry.core.coordinate \ import Coordinate from chiplotle.geometry.shapes.path import Path from chiplotle.geometry.core.group import Group from chiplotle.tools.mathtools.bezier_interpolation \ import bezier_interpolation import chiplotle.geometry.transforms as transforms from random import randint from copy import deepcopy import os # Object for the font. Character objects are stored inside class Font (object): def __init__ (self, path = False, resolution = False, scale = False): self.path = '' self.cache = True self.source = {} self.chars = {} self.resolution = 10 if resolution == False else resolution self.length = 0 self.name = '' self.cacheDir = 'shape_font_cache' #'~/tmp/shape_font_cache' self.cacheFilePath = '{0}/{1}-{2}.json' # Basepath, name, resolution if path <> False: self.load (path) if resolution <> False: self.render (resolution = resolution) if scale <> False: self.scale (scale) def load (self, path = False): if path <> False: self.path = path try: with open(self.path, 'r') as font_file: self.source = json.load (font_file) font_file.close() self.name = self.source["name"] print self.name, self.source["name"] for char in self.source["chars"]: char = Character (char) self.addChar (char) except: return False return True def write (self, path = False): self.scale (1) writer = FontWriter (self) return writer.write (path) def get (self, key): if key in self.chars: return deepcopy (self.chars[key]) else: return False def addChar (self, char): self.chars[char.key] = char def getChar (self, char): return self.get (ord (char)) def render (self, resolution = False): if resolution <> False: self.resolution = resolution if self.cache == True: if self.loadCache () == True: return True for key in self.chars: self.chars[key].render(self.resolution) if self.cache == True: self.writeCache () def scale (self, scale = 1): self.height = self.chars[self.chars.keys()[0]]._height * scale * 1.60 for key in self.chars: self.chars[key].scale = scale @property def cacheFile (self): return self.cacheFilePath.format (self.cacheDir, self.name, self.resolution) def loadCache (self): if self.cacheExists(): cache = json.load (open (self.cacheFile, 'r')) self.putCacheObject (cache) return True else: return False def writeCache (self): if self.cacheDirExists () == False: self.createCacheDir () handle = open (self.cacheFile, 'w') return json.dump (self.getCacheObject (), handle) def cacheExists (self): if os.path.exists (self.cacheFile): return True else: if self.cacheDirExists () == False: self.createCacheDir () return False def cacheDirExists (self): return os.path.exists (self.cacheDir) def createCacheDir (self): os.makedirs (self.cacheDir) def getCacheObject (self): return {char: self.chars[char].shape for char in self.chars} def putCacheObject (self, cache): for char in cache: self.chars[int (char)].shape = cache[char] # Character object. Character information is stored inside this object class Character (object): def __init__ (self, source = False): self.key = source["key"] self._width = source["width"] self._height = source["height"] self._lines = source["lines"] if 'margins' in source: self._margins = source["margins"] else: self._margins = [0,0,0,0] self.lines = deepcopy (self._lines) self.shape = [] self.curve_resolution = 10 self.length = 0 self._scale = 1 @property def width (self): return self._width * self.scale @property def height (self): return self._height * self.scale @property def margins (self): return [val * self.scale for val in self._margins] def render (self, resolution = False): if resolution <> False: self.curve_resolution = resolution if len (self.lines) > 0 and len (self.lines[0]) > 0: self.shape = [] position = (self._height, 0) for line in self.lines: points = [] for segment in line: if len(segment) > 2: # Curve if len (points) > 0: curve_points = [position] else: curve_points = [] for i in range (0,len(segment),2): curve_points.append ((segment[i], segment[i+1])) for point in bezier_interpolation (curve_points, self.curve_resolution, 1): points.append ((point[0] - position[0], point[1] - position[1])) #points.append ((point[0], point[1])) position = (point[0], point[1]) else: points.append ((segment[0] - position[0], segment[1] - position[1])) #points.append ((segment[0], segment[1])) position = (segment[0], segment[1]) self.shape.append(points) def hpgl (self, offset = (0,0)): buff = ['PA{0},{1}'.format (self.y + offset[0], self.x + offset[1])] for line in self.shape: buff.append ('PR{0:.1f},{1:.1f}'.format (float (line[0][0]), float (line[0][1]))) # switch to relative. points = ['{0:.1f},{1:.1f}'.format (float (point[0]), float (point[1])) for point in line[1:]] buff.append ('PD{0}'.format (','.join (points))) buff.append ('PU') return ';'.join (buff) @property def scale (self): return self._scale @scale.setter def scale (self, scale): self._scale = scale self.shape = [[(point[0] * scale, point[1] * scale) for point in line] for line in self.shape] # Loop through all lines, and points to scale them def validate (self): return True if self.patt.match (self.source) <> None else False # Whitespace buffer. Behaves like a character object class Whitespace (object): def __init__ (self, width = False): self.width = width if width <> False else 0 return 'PR0,{0:.1f}'.format (self.width) # Textbox. Wrapper for lines. class Textbox (object): alignLeft = 'left' alignCenter = 'center' alignRight = 'right' def __init__ (self, font = False, width = False, position = False, align = False, height = False, lineHeight = 1): if isinstance(position, Coordinate): self.position = position else: self.position = Coordinate (0, 0) self.width = width if width <> False else 0 self.maxHeight = height if height <> False else False self.height = 0 self.int_y = 0 self.lineHeight = lineHeight self._lines = [] if isinstance (font, Font): self.font = font else: self.font = None if align <> False: self.align = align else: self.align = self.alignLeft self.newLine() @property def lines (self): buff = [] for line in self._lines: if self.align == self.alignCenter or self.align == self.alignRight: line = deepcopy (line) delta_x = (self.width - line.int_x) * .5 if self.align == self.alignCenter else (self.width - line.int_x) line.offset ((0, delta_x)) buff.append (line) return buff def clear (self): self._lines = [] self.newLine() def newLine (self): if self.maxHeight == False or (self.int_y + self.font.height < self.maxHeight): self._lines.append (Textline (width = self.width, position = Coordinate (self.position[0] + self.int_y, self.position[1]))) self.int_y += self.font.height * self.lineHeight self.height = self.int_y return True else: return False def setFont (self, font): if isinstance (font, Font): self.font = font def setSize (self, size): if type (size) == int: self.size = size def insertText (self, text): if len (self._lines) == 0: self.newLine () for char in text: if char == '\n': if self.newLine() == False: # Could not add the new line. Return False return False else: charObj = self.font.getChar (char) if charObj <> False: if self._lines[-1].add (charObj) == False: if (self.newLine()): # Still space to add a new line if self._lines[-1].add (charObj) == False: # Could not fit the char on a second try return False else: # No space left for a new line return False return True def write (self, plotter): for line in self.lines: plotter.write (line.characters) def hpgl (self, offset = (0,0)): hpgl = [line.hpgl(offset) for line in self.lines] return ';'.join (hpgl) # Textline combines wrapper for characters class MultiTextbox (object): def __init__ (self, font = False, width = False, position = False, align = False, height = False, cols = 1, spacing = 400): self.font = font self.width = width if isinstance(position, Coordinate): self.position = position else: self.position = Coordinate (0, 0) self.width = width if width <> False else 0 self.height = height if height <> False else False self.maxCols = int (cols) self.spacing = int (spacing) self.cols = [] if isinstance (font, Font): self.font = font else: self.font = None if align <> False: self.align = align else: self.align = Textbox.alignLeft self.newCol() @property def colWidth (self): return (self.width - ((self.maxCols - 1) * self.spacing)) / self.maxCols def newCol (self): if len(self.cols) < self.maxCols: pos = Coordinate (self.position[0], self.position[1] + ((len(self.cols) * (self.colWidth + self.spacing)))) self.cols.append (Textbox (font = self.font, width = self.colWidth, position = pos, align = self.align, height = self.height)) return True else: return False def insertText (self, text): for char in text: if self.cols[-1].insertText (char) == False: if self.newCol (): if self.cols[-1].insertText (char) == False: return False else: return False class Textline (object): def __init__ (self, width = False, position = False): if isinstance(position, Coordinate): self.position = position else: self.position = Coordinate (0, 0) self.length = 0 self.int_x = 0 self.height = 0 self.width = width if width <> False else 0 self.characters = self.chars = [] def add (self, char): ## Tries to add the given character, if the character ## doens't fit it returns false if isinstance (char, Character): if self.room_for (char.width): self.int_x += char.margins[3] # Add left margins char.x = self.position[1] + self.int_x char.y = self.position[0] self.chars.append (char) self.int_x += char.width + char.margins[1] self.length += 1 return True; else: return False def calc_space (self, char): return char.unit * .75 def calc_space_width (self, key = -2): return self.chars[key].width def room_for (self, width): if self.int_x + width <= self.width: return True else: return False def insert_whitespace (self, width): self.chars.append (Whitespace (width)) def hpgl (self, offset = (0,0)): return ';'.join ([char.hpgl(offset) for char in self.chars]) def offset (self, offset): for char in self.chars: char.x += offset[1] char.y += offset[0] # Exports the font object to an JSON file class FontWriter (object): pointReplacementPatt = re.compile ("(\s+)(\-?\d+),\n\s+(\-?\d+)", flags=re.M) def __init__ (self, font = False): if font <> False: self.font = font def write (self, path): if self.font <> False: writeList = [] for char in self.font.chars: #writeObject = {} #writeObject['key'] = self.font.chars[char].key #writeObject['width'] = self.font.chars[char].width #writeObject['height'] = self.font.chars[char].height #writeObject['lines'] = self.font.chars[char].lines # writeObject = dict ( key = self.font.chars[char].key, width = self.font.chars[char].width, height = self.font.chars[char].height, lines = self.font.chars[char].lines, margins = self.font.chars[char].margins ) writeList.append (writeObject) self.writer = Writer ({'name': self.font.name, 'chars': writeList}) buff = self.pointReplacementPatt.sub ("\\1\\2,\\3", self.writer.build ()) try: with open (path, 'w') as self.f: self.f.write (buff) self.f.close () except IOError: return False return True # Exports a Python iterable to JSON. Currently supports list, tuple and dict class Writer (object): buff = '' newLine = '\n' tab = '\t' _stringBuff = '' __tabs = 0 _terminator = ',{0}'.format (newLine) _key = '"{0}": ' _list = ('[', ']') _dict = ('{', '}') def __init__ (self, value = False): if (value <> False): self.buff = value def write (self, path): self.build () try: with open (path, 'w') as self.f: self.f.write (self._stringBuff) self.f.close () except IOError: return False return True def build (self): self._stringBuff = self._writeValue (self.buff) return self._stringBuff def _tabs (self): return self.__tabs * self.tab def _increaseTabs (self, amount = 1): self.__tabs += amount def _decreaseTabs (self, amount = 1): self.__tabs += -1 * amount def _writeKey (self, key): return self._key.format (key) def _writeValue (self, value): if (type (value) == int): return self._writeInt (value) if (type (value) == float): return self._writeFloat (value) if (type (value) == str or type (value) == unicode): return self._writeStr (value) if (type (value) == list): return self._writeList (value) if (type (value) == dict): return self._writeDict (value) if (type (value) == tuple): return self._writeTuple (value) def _writeInt (self, value): return '{0:d}'.format (value) def _writeFloat (self, value): return '{0:n}'.format (value) def _writeStr (self, value): return '"{0}"'.format (value) def _writeList (self, _list): buffList = [] self._increaseTabs () buff = self._list[0] + self.newLine for value in _list: buffList.append (self._tabs() + self._writeValue (value)) buff += self._terminator.join (buffList) buff += self.newLine self._decreaseTabs () buff += self._tabs () + self._list[1] return buff def _writeTuple (self, _tuple): return self._writeList (_tuple) def _writeDict (self, _dict): buffList = [] self._increaseTabs () buff = self._dict[0] + self.newLine for key in _dict: buffList.append (self._tabs() + self._writeKey(key) + self._writeValue (_dict[key])) buff += self._terminator.join (buffList) buff += self.newLine self._decreaseTabs () buff += self._tabs () + self._dict[1] return buff def write (text, box, plotter): box.insertText (text) plotter.write (box.hpgl()) box.clear () def setFontSize (size, box, plotter): font = Font ('first.fnt') font.render (int (size)) box.setFont (font) def setPen (pen, plotter): plotter.write ('SP{0}'.format (int (pen))) def setForce (force, plotter): plotter.write ('FS{0}'.format (int (force))) def move (movement, plotter): plotter.write ('PU;PR{0},{1}'.format (movement[0], movement[1])) def getInput (box, plotter): mode = raw_input ('Select workingmode (w: write, m: move, s: set fontsize, p: set pen, f: set force, q: quit)') if (mode == 'w'): text = raw_input ('Text to write?') write (text, box, plotter) getInput (box, plotter) elif (mode == 'm'): movement = [raw_input ('Vertical movement'), raw_input ('Horizontal movement')] move (movement, plotter) getInput (box, plotter) elif (mode == 's'): size = raw_input ('New fontsize?') setFontSize (size, box, plotter) getInput (box, plotter) elif (mode == 'p'): pen = raw_input ('New pen?') setPen (pen, plotter) getInput (box, plotter) elif (mode == 'f'): pen = raw_input ('New force?') setForce (pen, plotter) getInput (box, plotter) elif (mode == 'q'): return if __name__ == '__main__': import chiplotle plotter = chiplotle.tools.plottertools.instantiate_virtual_plotter(type="HP7576A") #plotter = chiplotle.instantiate_plotters()[0] font = Font (path = '../handwriting.fnt', resolution = 10, scale = 10) #plotter.write ('AP0;SP4;FS7;cS5') textbox = Textbox (font=font, width = 3000, position = Coordinate (0, 0), align = Textbox.alignCenter, lineHeight = .75) #textBox.insertText ('In Doorn is een grote zoektocht aan de gang naar twee vermiste kinderen. Ze waren bij hun vader, die dood is aangetroffen in een recreatiegebied in de bossen. De man blijkt zelfmoord te hebben gepleegd.\n\nDe twee broertjes van 7 en 9 jaar oud uit Zeist zijn spoorloos. De politie doorzoekt het gebied met een helikopter, speurhonden en paarden. Ook het Korps Mariniers is ingezet.'.upper()) textbox.insertText ('Obama\nis\na\nsex-god') plotter.write ('SP1') plotter.write (textbox.hpgl()) #font.write ('../skeleton.fnt') #chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' #chars = 'S' #size = 1400 #scale = 60 #for char in chars: #if char in font.chars: ##scale = size / font.chars[char].length #shape = font.chars[char].group #transforms.scale (shape, scale) #transforms.offset (shape, offset) ##transforms.offset (shape, (offset[0] + (size - (font.chars[char].height * scale)), offset[1])) #width = font.chars[char].width * scale #plotter.write (shape) #offset = (offset[0], offset[1] + width) #for pen in range (1, 4): #plot = Group ([]) #offset = [-3500,-2200] #plotter.write ('AP0;SP{0};VS20;FS7'.format (pen)) #for char in chars: ##if char in font.chars: #for x in range (0,20): #old_char = deepcopy (font.chars[char]) ##for line in range (0, len (font.chars[char].lines)): ##for p in range (0, len (font.chars[char].lines[l])): ##for c in range (0, len (font.chars[char].lines[l][p])): ##font.chars[char].lines[l][p][c] += randint (-10,10) #for line in font.chars[char].lines: #for point in line: #for c in range (0, len (point)): #point[c] += randint (-15,15) #font.chars[char].render (20) #shape = font.chars[char].group #print shape #transforms.scale (shape, scale) #transforms.offset (shape, offset) #plot.append (deepcopy (shape)) ##offset[1] += 1000 #font.chars[char] = deepcopy (old_char) #plotter.write (plot) #for char in chars: ##if char in font.chars: #for y in range (0, 10): #for x in range (0,10): #old_char = deepcopy (font.chars[char]) ##for line in range (0, len (font.chars[char].lines)): ##for p in range (0, len (font.chars[char].lines[l])): ##for c in range (0, len (font.chars[char].lines[l][p])): ##font.chars[char].lines[l][p][c] += randint (-10,10) #for line in font.chars[char].lines: #for point in line: #for c in range (0, len (point)): #point[c] += randint (-15,15) #font.chars[char].render (15) #shape = font.chars[char].group #print shape #transforms.scale (shape, scale) #transforms.offset (shape, offset) #plotter.write (shape) #offset[1] += 1000 #font.chars[char] = deepcopy (old_char) #offset[1] = -5000 #offset[0] += 1200 #plotter.write ('SP2;VS1;FS7') #for char in chars: #if char in font.chars: #shape = font.chars[char].group #plotter.write (shape) #plotter.write ('SP3;VS1;FS7') #for char in chars: #if char in font.chars: #shape = font.chars[char].group #plotter.write (shape) #plotter.write ('SP4;VS1;FS7') #for char in chars: #if char in font.chars: #shape = font.chars[char].group #plotter.write (shape) chiplotle.io.view (plotter)