# -*- coding: utf-8 -*- """ $Id$ Copyright 2008-2010 Lode Leroy Copyright 2010 Lars Kruse <devel@sumpfralle.de> This file is part of PyCAM. PyCAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. PyCAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with PyCAM. If not, see <http://www.gnu.org/licenses/>. """ from pycam.Geometry import TransformableContainer from pycam.Geometry.Model import ContourModel from pycam.Geometry.Line import Line from pycam.Geometry.Point import Point TEXT_ALIGN_LEFT = 0 TEXT_ALIGN_CENTER = 1 TEXT_ALIGN_RIGHT = 2 class Letter(TransformableContainer): def __init__(self, lines): self.lines = lines def minx(self): return min([line.minx for line in self.lines]) def maxx(self): return max([line.maxx for line in self.lines]) def miny(self): return min([line.miny for line in self.lines]) def maxy(self): return max([line.maxy for line in self.lines]) def get_positioned_lines(self, base_point, skew=None): result = [] get_skewed_point = lambda p: \ Point(base_point.x + p.x + (p.y * skew / 100.0), base_point.y + p.y, base_point.z) for line in self.lines: skewed_p1 = get_skewed_point(line.p1) skewed_p2 = get_skewed_point(line.p2) # Some triplex fonts contain zero-length lines # (e.g. "/" in italict.cxf). Ignore these. if skewed_p1 != skewed_p2: new_line = Line(skewed_p1, skewed_p2) result.append(new_line) return result class Charset(object): def __init__(self, name=None, author=None, letterspacing=3.0, wordspacing=6.75, linespacingfactor=1.0, encoding=None): self.letters = {} self.letterspacing = letterspacing self.wordspacing = wordspacing self.linespacingfactor = linespacingfactor self.default_linespacing = 1.6 self.default_height = 10.0 if name is None: self.names = [] else: if isinstance(name, (list, set, tuple)): self.names = name else: self.names = [name] if author is None: self.authors = [] else: if isinstance(author, (list, set, tuple)): self.authors = author else: self.authors = [author] if encoding is None: self.encoding = "iso-8859-1" else: self.encoding = encoding def add_character(self, character, lines): if len(lines) > 0: self.letters[character] = Letter(lines) def get_names(self): return self.names def get_authors(self): return self.authors def render(self, text, origin=None, skew=0, line_spacing=1.0, pitch=1.0, align=None): result = ContourModel() if origin is None: origin = Point(0, 0, 0) if align is None: align = TEXT_ALIGN_LEFT base = origin letter_spacing = self.letterspacing * pitch word_spacing = self.wordspacing * pitch line_factor = self.default_linespacing * self.linespacingfactor \ * line_spacing for line in text.splitlines(): current_line = ContourModel() line_height = self.default_height for character in line: if character == " ": base = base.add(Point(word_spacing, 0, 0)) elif character in self.letters.keys(): charset_letter = self.letters[character] new_model = ContourModel() for line in charset_letter.get_positioned_lines(base, skew=skew): new_model.append(line, allow_reverse=True) for polygon in new_model.get_polygons(): # add polygons instead of lines -> more efficient current_line.append(polygon) # update line height line_height = max(line_height, charset_letter.maxy()) # shift the base position base = base.add(Point( charset_letter.maxx() + letter_spacing, 0, 0)) else: # unknown character - add a small whitespace base = base.add(Point(letter_spacing, 0, 0)) # go to the next line base = Point(origin.x, base.y - line_height * line_factor, origin.z) if not current_line.maxx is None: if align == TEXT_ALIGN_CENTER: current_line.shift(-current_line.maxx / 2, 0, 0) elif align == TEXT_ALIGN_RIGHT: current_line.shift(-current_line.maxx, 0, 0) else: # left align if current_line.minx != 0: current_line.shift(-current_line.minx, 0, 0) for polygon in current_line.get_polygons(): result.append(polygon) # the text should be just above the x axis if result.miny: # don't shift, if result.miny is None (e.g.: no content) or zero result.shift(0, -result.miny, 0) return result