def curve_func(step): subcurve_idx = step // nr_sides side_idx = step % nr_sides # distance from centre of polygons to centre of one side of outermost polygon = # radius * math.sin(corner_angle / 2) # therefore, distance from centre of polygons to corner of next-inner polygon = # radius * step_scale_factor # such that # step_scale_factor = math.sin(corner_angle / 2) / math.cos(math.pi / nr_sides - abs(step_rotate)) # where # step_rotate = math.pi / nr_sides * poly_shrink step_rotate = math.pi / nr_sides * poly_shrink corner_angle = (0.5 - 1 / nr_sides) * qah.circle step_scale_factor = math.sin( corner_angle / 2) / math.cos(math.pi / nr_sides - abs(step_rotate)) scale = step_scale_factor**subcurve_idx rotate = \ ( phase + step_rotate * subcurve_idx + qah.circle / nr_sides * side_idx ) return \ qah.Vector(radius * scale, 0).rotate(rotate)
def draw(self, cr, pos): self.strip() for drawColorLayers in (False, True): p = qh.Vector(pos.x, pos.y) for box in self.boxes: p.x = box.draw(cr, p, drawColorLayers)
def curve_func(n): subcurve = n // mod // k step = n * delta % mod phi = qah.circle * (step + subcurve) / mod theta = qah.circle * ((step + subcurve) * freq / mod + phase) r = offset + math.sin(theta) * amplitude return \ qah.Vector(r * math.cos(phi), r * math.sin(phi))
def get_page_number_pos(self, page, width): pos = qh.Vector(0, self.page_number_ypos) # Center the number relative to the text box. line = self.lines_per_page - 1 text_width = self.get_text_width(line) pos.x = self.get_text_start_pos(page, line) pos.x -= text_width / 2 # Center the box around the position pos.x -= width / 2 return pos
def draw(self, cr): logger.info("Page %d…", self.number) shaper = self.doc.shaper self.cr = cr if not self.lines: logger.debug("Leaving empty page blank") cr.show_page() return self.strip() lines = self.lines pos = qh.Vector(0, self.doc.top_margin) for i, line in enumerate(lines): pos.x = self.doc.get_text_start_pos(self, i) text_width = self.doc.get_text_width(i) line.draw(cr, pos, text_width) if line.get_quarter() or line.get_prostration(): self._show_quarter(line, line.get_quarter(), line.get_prostration(), pos.y) pos.y += line.height # Show page number. box = shaper.shape_word(format_number(self.number)) pos = self.doc.get_page_number_pos(self, box.width) box.draw(cr, pos) # Draw page decorations. if self.doc.page_decorations: o = 8 x = self.doc.get_text_start_pos(self, 0) + o y = self.doc.top_margin - self.doc.leading / 2 - o w = self.doc.get_text_width(0) + o * 2 h = self.doc.leading * self.doc.lines_per_page + o cr.save() rect = qh.Rect(x - w, y, w, h) cr.rectangle(rect) cr.set_line_width(1) cr.stroke() cr.rectangle(rect.inset((-5, -5))) cr.set_line_width(3) cr.stroke() cr.restore() cr.show_page()
def draw(self, cr, pos, width): offset = self.doc.leading / 2 height = self.height - offset linepos = qh.Vector(pos.x, pos.y) for line in self.boxes: line.draw(cr, linepos, width) linepos.x = pos.x linepos.y += line.height - offset / 1.2 cr.save() cr.set_line_width(0.5) cr.move_to((pos.x, pos.y - offset)) cr.rectangle(qh.Rect(pos.x - width, pos.y - offset, width, height)) cr.stroke() cr.restore()
def draw(self, cr): logger.info("Page %d…", self.number) shaper = self.doc.shaper self.cr = cr if not self.lines: logger.debug("Leaving empty page blank") cr.show_page() return lines = self.lines pos = qh.Vector(self.doc.text_start_pos, self.doc.top_margin) for i, line in enumerate(lines): line.draw(cr, pos) pos.y += line.height cr.show_page()
def shape_verse(self, verse, mark=None): """ Shapes a single verse and returns the corresponding nodes. """ buf = self.shape(verse, hb.HARFBUZZ.DIRECTION_RTL) nodes = [] infos = buf.glyph_infos positions = buf.glyph_positions flip = qh.Vector(1, -1) i = len(infos) - 1 while i >= 0: # Find all indices with same cluster j = i while j >= 0 and infos[i].cluster == infos[j].cluster: j -= 1 # Collect all glyphs in this cluster, iterating backwards to get # glyphs in the visual order. pos = qh.Vector(0, 0) glyphs = [] for k in reversed(range(i, j, -1)): glyphs.append( qh.Glyph(infos[k].codepoint, pos + flip * positions[k].offset)) pos += flip * positions[k].advance # The chars in this cluster chars = verse[infos[i].cluster:infos[j].cluster] # We skip space since the font kerns with it and we will turn these # kerns into glue below. if chars != " ": # Find the last non-combining mark char in the string, to check # for joining behaviour. for ch in chars: if not unicodedata.combining(ch): base = ch adv = self.font.get_glyph_h_advance(glyphs[-1].index) minadv = self.minfont.get_glyph_h_advance(glyphs[-1].index) maxadv = self.maxfont.get_glyph_h_advance(glyphs[-1].index) shrink = adv - minadv stretch = maxadv - adv if base in RIGH_JOINING or self.next_is_nonjoining( verse, infos, j): # Get the difference between the original advance width and # the advance width after OTL. kern = positions[k].advance - qh.Vector(adv, 0) # Re-adjust glyph positions. glyphs = [qh.Glyph(g.index, g.pos - kern) for g in glyphs] nodes.append( Box(self.doc, chars, glyphs, adv, stretch, shrink)) # Add glue with the kerning amount with minimal stretch and shrink. nodes.append( Glue(self.doc, kern.x, kern.x / 8.5, kern.x / 8.5)) else: nodes.append( Box(self.doc, chars, glyphs, pos.x, stretch, shrink)) elif pos.x != 0: # If space is not zero-width, add glue for it. nodes.append(Glue(self.doc, pos.x, pos.x / 8.5, pos.x / 8.5)) i = j if mark: buf = self.shape(mark, hb.HARFBUZZ.DIRECTION_LTR) glyphs, pos = buf.get_glyphs() nodes.append(Box(self.doc, mark, glyphs, pos.x)) return nodes