# -*- coding: utf-8 -*- import os import random import time from PIL import Image, ImageDraw, ImageFont from dotenv import load_dotenv from art_to_page import Drawing from defis_text import SplitText import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) load_dotenv(os.path.join(BASE_DIR, 'config.env')) class OpenImage(object): """Открываем картинку листка""" def __init__(self, path_save_image_folder, page_type, path_global_image_folder): self.path_save_image_folder = path_save_image_folder self.page_type = page_type self.path_global_image_folder = path_global_image_folder # self.REFLECTED_PAGE = REFLECTED_PAGE def _load_cell_list(self, REFLECTED_PAGE: bool): """Подгружаем лист в клетку""" if REFLECTED_PAGE: self.clear_paper = Image.open( os.path.join(self.path_global_image_folder, 'cell_reflected.jpg') ) else: self.clear_paper = Image.open( os.path.join(self.path_global_image_folder, 'cell.jpg') ) self.clear_paper.load() # draw = ImageDraw.Draw(self.clear_paper) return self.clear_paper def _load_line_list(self, REFLECTED_PAGE: bool): """Подгружаем лист в линию""" if REFLECTED_PAGE: self.clear_paper = Image.open( os.path.join(self.path_global_image_folder, 'line_reflected.jpg') ) else: self.clear_paper = Image.open( os.path.join(self.path_global_image_folder, 'line.jpg') ) self.clear_paper.load() # draw = ImageDraw.Draw(self.clear_paper) return self.clear_paper def open_image(self, REFLECTED_PAGE): if self.page_type == 'cell': return self._load_cell_list(REFLECTED_PAGE) if self.page_type == 'line': return self._load_line_list(REFLECTED_PAGE) def save_image(self, user_id, str_number): # global clear_paper path = os.path.join(self.path_save_image_folder, f'{user_id}_{int(time.time())}_{str_number}.jpg') self.clear_paper.save(path) self.clear_paper.close() return path class Fonts(object): def __init__(self, path_fonts_folder, font_name, size): self.path_fonts_folder = path_fonts_folder self.font_name = font_name self.size = size def _font_list(self) -> str: fonts = { "Merkucio": os.path.join(self.path_fonts_folder, "Merkucio Font4You.ttf"), "Abram": os.path.join(self.path_fonts_folder, "Abram.ttf"), "Gregory": os.path.join(self.path_fonts_folder, "Gregory.ttf"), "Lorenco": os.path.join(self.path_fonts_folder, "Lorenco.ttf"), "Merk": os.path.join(self.path_fonts_folder, "Merk.ttf"), "Salavat": os.path.join(self.path_fonts_folder, "Salavat.ttf"), } return fonts[self.font_name] def font(self): font_use = self._font_list() font_use = ImageFont.truetype(font_use, self.size, encoding='unic') return font_use class CreatePhoto(OpenImage, Fonts): REFLECTED_PAGE = False spaser_word = int(os.environ.get('spaser_word')) COLOR_USE = int(os.environ.get('COLOR_USE')) list_size = int(os.environ.get('list_size')) # height_list = int(os.environ.get('height_list')) def __init__(self, path_save_image_folder, path_fonts_folder, size, text, user_id, font_color, font_name, page_type, path_global_image_folder): self.text = text self.user_id = user_id self.font_color = font_color self.font_name = font_name self.page_type = page_type self.path_global_image_folder = path_global_image_folder OpenImage.__init__(self, path_save_image_folder, page_type, path_global_image_folder) Fonts.__init__(self, path_fonts_folder, font_name, size) def _config(self, page_type, font_use): if page_type == 'cell': offset = 34 # 39 if font_use == 'Merkucio': list_size = 1600 elif font_use == 'Abram': list_size = 1525 else: list_size = 1600 if page_type == 'line': offset = 94 if font_use == 'Merkucio': list_size = 1625 elif font_use == 'Abram': list_size = 1585 else: list_size = 1625 return offset, list_size def _get_split_text(self, font_use) -> list: """Разбиваем текст на строки""" text_split = [] for i in self.text.split('\n\n'): ST = SplitText(text=i, width_all=self.list_size, font_use=font_use, spaser_word=self.spaser_word) text_split.append(ST.split()) return text_split def create(self) -> list: # Подгружаем шрифт font_use = self.font() # Подгружаем картинку page = self.open_image(self.REFLECTED_PAGE) # print(self.page_type, font_use) offset, self.list_size = self._config(self.page_type, font_use) # номер сохранения страницы str_number = 0 paths = [] line_number = 0 # разбиваем на абзатцы text_split = self._get_split_text(font_use) # text_split.append( # split_a_text(text=i, width_all=self.list_size, # font_use=font_use, # spaser_word=self.spaser_word) # ) if self.page_type == 'cell': global_line_start = 300 if self.REFLECTED_PAGE else 75 elif self.page_type == 'line': global_line_start = 250 if self.REFLECTED_PAGE else 75 for abzats in text_split: for stroka in abzats: print(line_number) # Создаём новую страницу if (line_number == 20 and self.page_type == 'cell'): offset = 38 paths.append(self.save_image(self.user_id, str_number)) str_number += 1 # Смена страниц self.REFLECTED_PAGE = False if self.REFLECTED_PAGE else True page = self.open_image(self.REFLECTED_PAGE) line_number = 0 global_line_start = 300 if self.REFLECTED_PAGE else 75 if (line_number == 25 and self.page_type == 'line'): offset = 94 paths.append(self.save_image(self.user_id, str_number)) str_number += 1 # Смена страниц self.REFLECTED_PAGE = False if self.REFLECTED_PAGE else True page = self.open_image(self.REFLECTED_PAGE) print(line_number) line_number = 0 global_line_start = 250 if self.REFLECTED_PAGE else 75 # Задаём разные отступы вначале строки line_start = random.randint(global_line_start-15, global_line_start+15) # width, height = font_use.getsize(stroka) # if self.list_size-width > 150: # if stroka.split()[0][0].isupper(): # self.line_start = self.line_start + \ # random.randint(0, int((self.list_size-width)/5)) D = Drawing( COLOR_USE=self.COLOR_USE, stroka=stroka, font_use=font_use, list_size=self.list_size, clear_paper=page, line_start=line_start, offset=offset, spaser_word=self.spaser_word, font_color=self.font_color, font_name=self.font_name ) D.draw() # Выбираем отсуп по типу листа if self.page_type == 'cell': offset += 118 if self.page_type == 'line': # После пятой строчки увеличивает отступ if line_number <= 10: offset += 87 elif line_number <= 18: offset += 88 else: offset += 88 line_number += 1 paths.append( self.save_image(self.user_id, str_number) ) return paths # text = """Как принято считать, сделанные на базе интернет-аналитики выводы являются только методом политического участия и превращены в посмешище, хотя само их существование приносит несомненную пользу обществу. Сложно сказать, почему интерактивные прототипы смешаны с не уникальными данными до степени совершенной неузнаваемости, из-за чего возрастает их статус бесполезности. В целом, конечно, синтетическое тестирование в значительной степени обусловливает важность анализа существующих паттернов поведения. # Как уже неоднократно упомянуто, акционеры крупнейших компаний, вне зависимости от их уровня, должны быть подвергнуты целой серии независимых исследований. Не следует, однако, забывать, что разбавленное изрядной долей эмпатии, рациональное мышление в значительной степени обусловливает важность как самодостаточных, так и внешне зависимых концептуальных решений. Каждый из нас понимает очевидную вещь: разбавленное изрядной долей эмпатии, рациональное мышление требует определения и уточнения модели развития. Значимость этих проблем настолько очевидна, что начало повседневной работы по формированию позиции не оставляет шанса для направлений прогрессивного развития! Прежде всего, высококачественный прототип будущего проекта говорит о возможностях дальнейших направлений развития. # Задача организации, в особенности же дальнейшее развитие различных форм деятельности играет определяющее значение для дальнейших направлений развития. Принимая во внимание показатели успешности, сложившаяся структура организации говорит о возможностях благоприятных перспектив. # """ # absFilePath = os.path.abspath(__file__) # BASE_DIR = os.path.dirname(absFilePath) # CP = CreatePhoto( # path_save_image_folder=os.path.join(BASE_DIR, 'IMG'), # path_global_image_folder=os.path.join(BASE_DIR, 'PAGES'), # path_fonts_folder=os.path.join(BASE_DIR, 'Fonts'), # size=85, # text=text, # user_id=123, # font_color='49, 88, 143', # font_name='Merkucio', # page_type='cell') # print(CP.create())
Public
Python's built-in pathlib
library is nice! It's less verbose than os.path
and is easier to read, for example: root = Path('root'); subdir = root / 'subfolder1' / 'subfolder2'; subdir.mkdir(exist_ok=True)
. Use it!
Type hints help humans and linters (like mypy
) to understand what to expect "in" and "out" for a function. Not only it serves as a documentation for others (and you after some time, when the code is wiped from your "brain cache"), but also allows using automated tools to find type errors.
Dataclasses let you get rid of many boilerplate code, most often the "init hell": def __init__(self, a): self.a = a
. With dataclasses, it's all done automatically!
Copy-paste lead to errors very, very often because developers forget to change value on one of copy-pasted lines . You should avoid it as much as possible. Usually a good solution is to extract the things that differ into separate variables.
Extract common code, don't repeat it.
Suggested change:file = 'cell_reflected.jpg' if REFLECTED_PAGE else 'cell.jpg' self.clear_paper = Image.open( os.path.join(self.path_global_image_folder, file) )
Side effects are evil: it's very hard to track what function does under the hood, because function signature does not reflect side effects. Always try to write "pure" functions, i.e. those which receive inputs, do something internally, and return output. You should be able to run such function million times and get exactly the same result with nothing changed in the environment. However, side effects are totally OK if that's the only thing the function does.
Should either set self.clear_paper
, or return it, not both.
Python doesn't check whether you inspected all possible values of some collection. If a variable can become "one" or "two", and you check if var == "one": ... elif var == "two": ...
then this code will break when one day you allow variable to be "three". You should always check whether the variable holds something unexpected, and in such case an exception should be raised: else: raise ValueError(f'Unexpected value: {var}')
.
This code is not really needed or may be simplified
Inheriting from object
is redundant
class Fonts:
Key constants of a class should be so-called "class variables" instead of using/defining them inside methods. If you create a child class and want to change those variables, you'll have to rewrite entire methods. With class vars, you only have to overwrite those class vars in child. Example: class MyClass: SOME_VALUE = 1
and class MySubclass(Class): SOME_VALUE = 2
Magic variables (usually numeric values without explanation why the values are exactly like that) are evil. One should make them class / module variables or put in some config / env files with explanation why these values were chosen.
Some methods within a class don't depend on self
variable. This is an indication that such methods should be classmethods. Use the @classmethod
decorator.
@classmethod def _get_split_text(cls, text, font_use) -> list:
print()
is a nice way to output to stdout. But one day you'll need to not only write to stdout, but also, say, to a file. Another day you'll need to output only severe errors' messages, and nothing else. This all could be solved if using logging
module. Usually it's as easy as from logging import getLogger; log = getLogger(__name__)
.
Create new review request