# -*- coding: utf-8 -*- # Copyright 2017 Mir Calculate. http://www.calculate-linux.org # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys import os import re from calculate.lib.cl_template import TemplateFormat import hashlib from calculate.lib.cl_lang import setLocalTranslate from calculate.lib.utils.files import (getProgPath, pathJoin, process, STDOUT, readFile, writeFile, listDirectory) _ = lambda x: x setLocalTranslate('cl_lib3', sys.modules[__name__]) class backgrounds(TemplateFormat): """ Формат для модификации принадлежности файлов пакетам """ text = "" source = None convert = None prefix = None stretch = False mirror = False fallback_md5fn = "md5sum" def prepare(self): self.chroot_cmd = None self.chroot_path = None self.convert_cmd = getProgPath('/usr/bin/convert') self.bash_cmd = getProgPath('/bin/bash') self.identify_cmd = getProgPath('/usr/bin/identify') self.source_width = None self.source_height = None def textToXML(self): return self.text def setRootPath(self, rpath): self.convert_cmd = '/usr/bin/convert' self.identify_cmd = '/usr/bin/identify' if not os.path.exists(pathJoin(rpath, self.convert_cmd)): self.convert_cmd = None if not os.path.exists(pathJoin(rpath, self.identify_cmd)): self.identify_cmd = None self.chroot_cmd = getProgPath('/bin/chroot') self.chroot_path = rpath def setMirror(self): self.mirror = True def setSource(self, source): self.source = source def setConvert(self, convert): self.convert = convert def setPrefix(self, prefix): self.prefix = prefix def getImageMD5(self, source, resolutions=()): if source: data = readFile(source) else: data = "" return hashlib.md5(data+"\n".join(resolutions)).hexdigest() def setStretch(self, stretch): self.stretch = stretch def trim_chroot_path(self, p): return "/%s" % os.path.relpath(p, self.chroot_path) def _create_image(self, source, target, width, height, force=False): res = "%dx%d" % (width, height) if not force and (width > self.source_width or height > self.source_height): return False if self.convert != "gfxboot" and ( (width == self.source_width and height == self.source_height) and (source.rpartition('.')[2] == target.rpartition('.')[2])): with writeFile(target) as sf: sf.write(readFile(source)) return True if self.chroot_cmd: source = self.trim_chroot_path(source) target = self.trim_chroot_path(target) command = [self.convert_cmd, "-quality", "95", source, "-resize", "%s^" % res, "-strip", "-gravity", "center", "-crop", "%s+0+0" % res] if self.convert == "gfxboot": command.extend( ["-sampling-factor", "2x2", "-interlace", "none", "-set", "units", "PixelsPerSecond"]) command += [target] if self.chroot_cmd: convert = process(self.chroot_cmd, self.chroot_path, self.bash_cmd, "-c", " ".join(command), stderr=STDOUT) else: convert = process(*command, stderr=STDOUT) convert.write(self.text) if convert.success(): return True return False def get_image_resolution(self, source): if self.chroot_cmd: source = self.trim_chroot_path(source) identify = process(self.chroot_cmd, self.chroot_path, self.bash_cmd, "-c", " ".join([self.identify_cmd, "-format '%w %h'", source])) else: identify = process(self.identify_cmd, "-format", "%w %h", source) if identify.success(): swidth, _sep, sheight = identify.read().strip().partition(" ") if swidth.isdigit() and sheight.isdigit(): return int(swidth), int(sheight) return None, None return None, None def processingFile(self, textConfigFile, rootPath=None, nameFile=None): """Обработка конфигурационного файла""" if not (self.convert_cmd and self.identify_cmd): self.setError("%s. %s" % (_("The 'backgrounds' format is unavailable"), _("Need to install {packages}").format( packages="virtual/imagemagick-tools"))) return False if not self.source: if not self.mirror: return "" if not self.convert: self.convert = self.source.rpartition(".")[2].lower() if self.convert not in self.objVar.Get('cl_image_formats'): self.setError(_("Wrong image format '%s'") % self.convert) return False reRule = re.compile(r"^(\d+)x(\d+)(?:-[0-9]+)?$") if not rootPath: rootPath = '/' if self.prefix is None: workdir, prefix = os.path.split(nameFile) else: workdir = nameFile prefix = "" if prefix: md5_fn = os.path.join(workdir, "%s.md5" % re.sub(r"[-._]+$", "", prefix)) else: md5_fn = os.path.join(workdir, self.fallback_md5fn) if not self.source: source = None else: source = os.path.normpath(pathJoin(rootPath, self.source)) if not os.path.exists(source): source = None else: self.source_width, self.source_height = \ self.get_image_resolution(source) if not self.source_width: self.setError( _("Failed to detect resolution for image '%s'") % source) return False if self.text.strip(): text_list = self.text.split("\n") else: text_list = self.objVar.Get('cl_resolutions') image_files = [] resolutions = [x.strip() for x in text_list if not x.startswith("#") and x.strip()] image_md5 = self.getImageMD5(source, resolutions=resolutions) only_one = len(resolutions) == 1 and self.text.strip() if only_one: image_fn = pathJoin(workdir, prefix) fn_base = image_fn.rpartition(".")[0] if not fn_base: fn_base = image_fn md5_fn = "%s.md5" % fn_base if os.path.exists(md5_fn): md5sum = readFile(md5_fn).strip() if md5sum == image_md5: return "" if only_one: removed_files = [pathJoin(workdir, prefix)] else: removed_files = [] extensions = self.objVar.Get('cl_image_formats') re_remove = re.compile(r"^%s\d{3,4}x\d{3,4}\.(%s)$" % (prefix, "|".join(extensions))) for remove_fn in listDirectory(workdir, fullPath=True): fn = os.path.split(remove_fn)[1] if re_remove.match(fn): removed_files.append(remove_fn) created = set() if source: for resolution in resolutions: if resolution == "original": resolution = "{}x{}".format(self.source_width, self.source_height) rule_match = reRule.search(resolution) if not rule_match: self.setError( _("Wrong 'backgrounds' resolution: %s") % resolution) return False width, height = rule_match.groups() width = int(width) height = int(height) if (width,height) in created: continue else: created.add((width,height)) if only_one: image_fn = pathJoin(workdir, prefix) else: ext_map = {'gfxboot': 'jpg'} image_fn = pathJoin(workdir, "%s%s.%s" % ( prefix, resolution, ext_map.get(self.convert, self.convert))) if self._create_image(source, image_fn, width, height, force=self.stretch): if image_fn in removed_files: removed_files.remove(image_fn) image_files.append(image_fn) else: self.parent.printWARNING( _("Creation of image with %s resolution skipped") % ( "%dx%d" % (width, height))) with writeFile(md5_fn) as f: f.write("%s\n" % image_md5) else: removed_files.append(md5_fn) self.changed_files.append(md5_fn) for remove_fn in removed_files: try: if os.path.lexists(remove_fn): os.unlink(remove_fn) except (IOError, OSError): self.parent.printWARNING(_("Failed to remove %s") % remove_fn) self.changed_files.extend(image_files) self.changed_files.extend(removed_files) return ""