You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
264 lines
10 KiB
264 lines
10 KiB
# -*- 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 ..cl_template import TemplateFormat
|
|
import hashlib
|
|
|
|
from ..cl_lang import setLocalTranslate
|
|
from ..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, binary=True)
|
|
else:
|
|
data = b""
|
|
return hashlib.md5(data + "\n".join(resolutions).encode("UTF-8")).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, binary=True) as sf:
|
|
sf.write(readFile(source, binary=True))
|
|
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("^(\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("[-._]+$", "", 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 ""
|