# -*- coding: utf-8 -*- # -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- # # This file is part of the LibreOffice project. # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # """ Pack images into archives. """
from __future__ import with_statement
import argparse from collections import OrderedDict import contextlib import logging import os import shutil import sys import tempfile import zipfile
replace_zip_file(tmp_output_file, args.output_file) try:
LOGGER.info("Remove temporary directory %s", tmp_dir)
shutil.rmtree(tmp_dir) except Exception:
LOGGER.error("Unable to remove temporary directory %s", tmp_dir)
sys.exit(2) else:
LOGGER.info("No rebuild needed. %s is up to date.", args.output_file)
def check_rebuild(zip_file, imagelist_filenames, custom_paths, zip_list, links_file): """ Check if a rebuild is needed.
:type zip_file: str
:param zip_file: File to check against (use st_mtime).
:type imagelist_filenames: dict
:param imagelist_filenames: List of imagelist filename.
:type custom_paths: list
:param custom_paths: List of paths to use with links.txt files.
:type zip_list: dict
:param zip_list: List of filenames to create the zip archive.
:type links_file: str
:param links_file: filename to read the links from
:rtype: bool
:return: Trueif rebuild is needed andFalseifnot. """
ifnot os.path.exists(zip_file): returnTrue
zip_file_stat = os.stat(zip_file)
def compare_modtime(filenames): """ Check if modification time of zip archive is older the provided
list of files.
:type filenames: dict
:param filenames: List of filenames to check against.
:rtype: bool
:return: Trueif zip archive is older andFalseifnot. """ for filename, path in filenames.items():
filename = os.path.join(path, filename) if filename else path if os.path.exists(filename) and zip_file_stat.st_mtime < os.stat(filename).st_mtime: returnTrue returnFalse
if compare_modtime(imagelist_filenames): returnTrue if compare_modtime(zip_list): returnTrue if links_file isnotNone: if zip_file_stat.st_mtime < os.stat(links_file).st_mtime: returnTrue else: for path in custom_paths:
link_file = os.path.join(path, 'links.txt') if os.path.exists(link_file): if zip_file_stat.st_mtime < os.stat(link_file).st_mtime: returnTrue
returnFalse
def replace_zip_file(src, dst): """ Replace the old archive file with the newly created one.
:type src: str
:param src: Source file name.
:type dst: str
:param dst: Destination file name. """
LOGGER.info("Replace old zip archive with new archive") if os.path.exists(dst): try:
os.unlink(dst) except OSError as e: if os.path.exists(src):
os.unlink(src)
LOGGER.error("Unable to unlink old archive '%s'", dst)
LOGGER.error(str(e))
sys.exit(1)
try:
LOGGER.info("Copy archive '%s' to '%s'", src, dst)
shutil.move(src, dst) except (shutil.SameFileError, OSError) as e:
os.unlink(src)
LOGGER.error("Cannot copy file '%s' to %s: %s", src, dst, str(e))
sys.exit(1)
def optimize_zip_layout(zip_list, sort_file=None): """ Optimize the zip layout by ordering the list of filename alphabetically orwith provided sort file (can be partly).
:type zip_list: dict
:param zip_list: List of files to include in the zip archive.
:type sort_file: str
:param sort_file: Path/Filename to file with sort order.
:rtype: OrderedDict
:return: Dict with right sort order. """ if sort_file isNone:
LOGGER.info("No sort file provided")
LOGGER.info("Sorting entries alphabetically")
LOGGER.info("Sort entries from file '%s'", sort_file)
sorted_zip_list = OrderedDict() try:
fh = open(sort_file) except IOError:
LOGGER.error("Cannot open file %s", sort_file)
sys.exit(1) else: with fh: for line in fh:
line = line.strip() if line == ''or line.startswith('#'): continue
if line in zip_list:
sorted_zip_list[line] = '' else:
LOGGER.warning("Unknown file '%s'", line)
for key in sorted(zip_list.keys()): if key notin sorted_zip_list:
sorted_zip_list[key] = ''
return sorted_zip_list
def create_zip_archive(zip_list, links, tmp_dir, tmp_zip_file, sort_file=None): """ Create the zip archive.
:type zip_list: dict
:param zip_list: All filenames to be included in the archive.
:type links: dict
:param links: All filenames to create links.txt file.
:type tmp_dir: str
:param tmp_dir: Path to temporary saved files.
:type tmp_zip_file: str
:param tmp_zip_file: Filename/Path of temporary zip archive.
:type sort_file: str|None
:param sort_file: Optional filename with sort order to apply. """
LOGGER.info("Creating image archive")
with contextlib.closing(zipfile.ZipFile(tmp_zip_file, 'w')) as tmp_zip: if links.keys():
LOGGER.info("Add file 'links.txt' to zip archive")
create_links_file(tmp_dir, links)
tmp_zip.write('links.txt', compress_type=zipfile.ZIP_DEFLATED)
for link in ordered_zip_list:
LOGGER.info("Add file '%s' from path '%s' to zip archive", link, tmp_dir) try: if (link.endswith(".svg")):
tmp_zip.write(link, compress_type=zipfile.ZIP_DEFLATED) else:
tmp_zip.write(link) except OSError:
LOGGER.warning("Unable to add file '%s' to zip archive", link)
os.chdir(old_pwd)
def create_links_file(path, links): """ Create file links.txt. Contains all links.
:type path: str
:param path: Path where to create the file.
def remove_links_from_zip_list(zip_list, links): """ Remove any files from zip list that are linked.
:type zip_list: dict
:param zip_list: Files to include in the zip archive.
:type links: dict
:param links: Images which are linked. """
LOGGER.info("Remove linked files from zip list")
for link in links.keys(): if link in zip_list:
module = zip_list[link] del zip_list[link] # tdf#135369 if we removed something that is a link to a real file # from our list of images to pack, but that real image is not in # the list of images to pack, add it in instead now if links[link] notin zip_list:
zip_list[links[link]] = module
LOGGER.debug("Cleaned zip list:\n%s", "\n".join(zip_list))
def create_zip_list(global_image_list, module_image_list, custom_image_list, global_path, module_path): """ Create list of images for the zip archive.
:type global_image_list: dict
:param global_image_list: Global images.
def check_links(links): """ Check that no link points to another link.
:type links: dict
:param links: Links to icons """
stop = False
for link, target in links.items(): if target in links.keys():
LOGGER.error("Link %s -> %s -> %s", link, target, links[target])
stop = True
if stop:
LOGGER.error("Some icons in links.txt were found to link to other linked icons")
sys.exit(1)
def read_links(links, filename): """ Read links from file.
:type links: dict
:param links: Hash to store all links
:type filename: str
:param filename: filename to read the links from """
LOGGER.info("Read links from file '%s'", filename) ifnot os.path.isfile(filename):
LOGGER.info("No file to read") return
try:
fh = open(filename) except IOError:
LOGGER.error("Cannot open file %s", filename)
sys.exit(1) else: with fh: for line in fh:
line = line.strip() if line == ''or line.startswith('#'): continue
tmp = line.split(' ') if len(tmp) == 2:
links[tmp[0]] = tmp[1] else:
LOGGER.error("Malformed links line '%s' in file %s", line, filename)
sys.exit(1)
LOGGER.debug("Read links:\n%s", "\n".join(links))
def find_custom(custom_paths): """ Find all custom images.
:type custom_paths: list
:param custom_paths: List of custom paths to search within.
:rtype: dict
:return: List of all custom images. """
LOGGER.info("Find all images in custom paths")
custom_image_list = {} for path in custom_paths: # find all png files under the path including subdirs
custom_files = [val for sublist in [
[os.path.join(i[0], j) for j in i[2] if j.endswith('.png') and os.path.isfile(os.path.join(i[0], j))] for i in os.walk(path)] for val in sublist]
for filename in custom_files: if filename.startswith(path):
key = filename.replace(os.path.join(path, ''), '') if key notin custom_image_list:
custom_image_list[key] = path
def parse_image_list(imagelist_filenames): """ Parse and extract filename from the imagelist files.
:type imagelist_filenames: list
:param imagelist_filenames: List of imagelist files.
:rtype: tuple
:return: Tuple with dicts containing the globalor module image list. """
global_image_list = {}
module_image_list = {} for imagelist_filename in imagelist_filenames.keys():
LOGGER.info("Parsing '%s'", imagelist_filename)
try:
fh = open(imagelist_filename) except IOError:
LOGGER.error("Cannot open imagelist file %s", imagelist_filename)
sys.exit(2) else:
line_count = 0 with fh: for line in fh:
line_count += 1
line = line.strip() if line == ''or line.startswith('#'): continue # clean up backslashes and double slashes
line = line.replace('\\', '/')
line = line.replace('//', '/')
if line.startswith('%GLOBALRES%'):
key = "res/%s" % line.replace('%GLOBALRES%/', '')
global_image_list[key] = True if key.endswith('.png'):
global_image_list[key[:-4] + '.svg'] = True continue
if line.startswith('%MODULE%'):
key = line.replace('%MODULE%/', '')
module_image_list[key] = True if key.endswith('.png'):
module_image_list[key[:-4] + '.svg'] = True continue
LOGGER.error("Cannot parse line %s:%d", imagelist_filename, line_count)
sys.exit(1)
def get_imagelist_filenames(filename): """ Extract a list of imagelist filenames.
:type filename: str
:param filename: Name of file from extracting the list.
:rtype: dict
:return: List of imagelist filenames. """
LOGGER.info("Extract the imagelist filenames")
imagelist_filenames = {} try:
fh = open(filename) except IOError:
LOGGER.error("Cannot open imagelist file %s", filename)
sys.exit(1) else: with fh: for line in fh:
line = line.strip() ifnot line or line == '': continue
for line_split in line.split(' '):
line_split.strip()
imagelist_filenames[line_split] = ''
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.