├── CNAME
├── docs
├── requirements.txt
├── source
│ ├── _static
│ │ └── fp.png
│ ├── package.rst
│ ├── install.rst
│ ├── fontpreview.rst
│ ├── index.rst
│ ├── conf.py
│ ├── cli.rst
│ └── example.rst
├── Makefile
└── make.bat
├── .github
├── PULL_REQUEST_TEMPLATE.md
└── ISSUE_TEMPLATE
│ ├── fontpreview-enhancement.md
│ └── fontpreview-issue.md
├── .circleci
└── config.yml
├── fontpreview
├── __init__.py
├── fontpreview.py
├── fontbanner.py
└── fontpage.py
├── CHANGES.md
├── setup.py
├── CODE_OF_CONDUCT.md
├── .gitignore
├── CONTRIBUTING.md
├── bin
└── fp
├── README.md
├── test_fp.py
└── LICENSE.md
/CNAME:
--------------------------------------------------------------------------------
1 | fontpreview.readthedocs.io
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | fontpreview
2 | Pillow
--------------------------------------------------------------------------------
/docs/source/_static/fp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MatteoGuadrini/fontpreview/HEAD/docs/source/_static/fp.png
--------------------------------------------------------------------------------
/docs/source/package.rst:
--------------------------------------------------------------------------------
1 | fontpreview package
2 | ===================
3 |
4 | This package contains three modules that allow, through specific classes, to create simple and advanced font previews.
5 |
6 | .. toctree::
7 | :maxdepth: 2
8 | :caption: Contents:
9 |
10 | fontpreview
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | # Title of Pull Request
2 |
3 | ## List of changes
4 |
5 | - This pull request has a changes in the format: `changes is...`.
6 | - ✅ `Add function on fontpreview`
7 | - ✅ `Add parameter -p/--parameter`
8 | - ✅ `Update readme.md`
9 | - ❌ `Delete part of documentation`
10 |
11 | ### Description of the proposed features
12 |
13 | Descriptive text of the features.
14 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/fontpreview-enhancement.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: fontpreview enhancement
3 | about: fontpreview enhancement template
4 | title: fontpreview enhancement
5 | labels: enhancement
6 | assignees: MatteoGuadrini
7 | ---
8 |
9 | ## Description
10 |
11 | Description of the proposal
12 |
13 |
14 | ## Proposed names of the parameters (short and long)
15 |
16 | * name parameter
17 | * possible argument(s)
18 |
19 | Additional context
20 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/fontpreview-issue.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: fontpreview bug
3 | about: fontpreview bug template
4 | title: fontpreview bug
5 | labels: bug
6 | assignees: MatteoGuadrini
7 | ---
8 |
9 | ## Description
10 |
11 | Description of problem
12 |
13 | ## Steps to Reproduce
14 |
15 | Line of code
16 |
17 | ## Expected Behaviour
18 |
19 | Description of what is expected
20 |
21 | ## Your Environment
22 |
23 | * fontpreview version used:
24 | * Operating System and version:
25 |
26 | Additional context
27 |
--------------------------------------------------------------------------------
/docs/source/install.rst:
--------------------------------------------------------------------------------
1 | Installation
2 | ############
3 |
4 | Here are the installation instructions
5 |
6 | .. toctree::
7 | :maxdepth: 2
8 | :caption: Contents:
9 |
10 | Python
11 | ******
12 |
13 | *fontpreview* is written in python3 (3.6 and higher). The only external library required is
14 | `Pillow `_ (fork of PIL):
15 |
16 |
17 | Installation
18 | ************
19 |
20 | .. code-block:: console
21 |
22 | $ pip install --user fontpreview
23 |
24 | .. note::
25 | If you want to use the command line tool, you need to install the system-wide library: ``pip install fontpreview``
26 |
27 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | orbs:
4 | python: circleci/python@0.2.1
5 |
6 | jobs:
7 | build-and-test:
8 | executor: python/default
9 | steps:
10 | - checkout
11 | - run: sudo python setup.py install
12 | - run: sudo chmod -R 777 /tmp
13 | - run: wget https://github.com/googlefonts/noto-fonts/raw/master/hinted/ttf/NotoSans/NotoSans-Regular.ttf -P /tmp
14 | - run: echo /tmp/NotoSans-Regular.ttf | python -m unittest test_fp.py
15 | - run: fp --version
16 | - run: fp /tmp/NotoSans-Regular.ttf --save /tmp/noto_preview.png
17 | - run: ls /tmp/noto_preview.png
18 |
19 | workflows:
20 | main:
21 | jobs:
22 | - build-and-test
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = source
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/source/fontpreview.rst:
--------------------------------------------------------------------------------
1 | fontpreview modules
2 | ===================
3 |
4 | fontpreview
5 | ___________
6 |
7 | fontpreview module contains **FontPreview** class
8 |
9 | .. automodule:: fontpreview.fontpreview
10 | :members:
11 | :special-members:
12 | :show-inheritance:
13 |
14 | fontbanner
15 | __________
16 |
17 | fontbanner module contains **FontBanner**, **FontLogo** and **FontWall** class
18 |
19 | .. automodule:: fontpreview.fontbanner
20 | :members:
21 | :special-members:
22 | :show-inheritance:
23 |
24 | fontpage
25 | ________
26 |
27 | fontpage module contains **FontPage** and **FontPageTemplate** class
28 |
29 | .. automodule:: fontpreview.fontpage
30 | :members:
31 | :special-members:
32 | :show-inheritance:
33 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | .. fontpreview documentation master file, created by
2 | sphinx-quickstart on Tue Nov 17 09:43:27 2020.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to fontpreview's documentation!
7 | =======================================
8 |
9 | *fontpreview* is a python library, which allows you to create simple and advanced previews of specific fonts.
10 |
11 | In addition, the library includes some classes that allow the advanced creation of preview pages of the characters that make up a font.
12 |
13 | .. toctree::
14 | :maxdepth: 2
15 | :caption: Contents:
16 |
17 | install
18 | example
19 | cli
20 | package
21 |
22 |
23 |
24 | Indices and tables
25 | ==================
26 |
27 | * :ref:`genindex`
28 | * :ref:`modindex`
29 | * :ref:`search`
30 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=source
11 | set BUILDDIR=build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/fontpreview/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- encoding: utf-8 -*-
3 | # vim: se ts=4 et syn=python:
4 |
5 | # created by: matteo.guadrini
6 | # __init__.py -- fontpreview
7 | #
8 | # Copyright (C) 2020 Matteo Guadrini
9 | #
10 | # This program is free software: you can redistribute it and/or modify
11 | # it under the terms of the GNU General Public License as published by
12 | # the Free Software Foundation, either version 3 of the License, or
13 | # (at your option) any later version.
14 | #
15 | # This program is distributed in the hope that it will be useful,
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | # GNU General Public License for more details.
19 | #
20 | # You should have received a copy of the GNU General Public License
21 | # along with this program. If not, see .
22 | from pkg_resources import get_distribution, DistributionNotFound
23 |
24 | from .fontpreview import FontPreview
25 | from .fontbanner import FontBanner, FontLogo, FontWall
26 | from .fontpage import FontPage, FontPageTemplate, FontBooklet
27 |
28 | try:
29 | __version__ = get_distribution('fontpreview').version
30 | except DistributionNotFound:
31 | __version__ = 'version not found'
32 |
--------------------------------------------------------------------------------
/CHANGES.md:
--------------------------------------------------------------------------------
1 | # Release notes
2 |
3 | ## 1.2.0
4 | Dec 12, 2020
5 |
6 | - Additions show method on *FontPreview*, *FontWall*, *FontPage* class
7 | - Additions _FontBooklet_ class
8 | - Fix FontPageTemplate *\__str\__* method
9 | - Fix message raises on set_header/logo/body/footer method on _FontPage_ class
10 |
11 | ## 1.1.0
12 | Dec 12, 2020
13 |
14 | - Additions declaration mode for *FontPage* class
15 | - Additions *test_hex_color* method on TestFontPreview class
16 | - Additions *test_hex_color* method on TestFontPreview class
17 | - Additions *test_declarative_object* method on TestFontPreview class
18 | - Additions *test_other_color_system* method on TestFontPreview class
19 | - Fix TestFontPreview.test_font_size method and FontPage.__compose method
20 | - Fix test_text_position method on TestFontPreview class
21 |
22 | ## 1.0.0
23 | Dec 5, 2020
24 |
25 | - *fontpreview* python package is available.
26 | - *fp* command line tool is available.
27 | - *FontPreview* class represents a preview object of a given font.
28 | - *FontBanner* class represents a preview banner object of a given font. Based on *FontPreview* class.
29 | - *FontLogo* class represents a logo object of a given font. Based on *FontPreview* class.
30 | - *FontWall* class represents a wall of preview fonts object.
31 | - *FontPage* class represents a page of preview fonts object.
32 | - *FontPageTemplate* class represents a template for *FontPage* class.
33 | - Documentation has been created
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- encoding: utf-8 -*-
3 | # vim: se ts=4 et syn=python:
4 |
5 | # created by: matteo.guadrini
6 | # setup -- fontpreview
7 | #
8 | # Copyright (C) 2020 Matteo Guadrini
9 | #
10 | # This program is free software: you can redistribute it and/or modify
11 | # it under the terms of the GNU General Public License as published by
12 | # the Free Software Foundation, either version 3 of the License, or
13 | # (at your option) any later version.
14 | #
15 | # This program is distributed in the hope that it will be useful,
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | # GNU General Public License for more details.
19 | #
20 | # You should have received a copy of the GNU General Public License
21 | # along with this program. If not, see .
22 |
23 | from setuptools import setup
24 | __version__ = '1.2.0'
25 |
26 | with open("README.md", "r") as fh:
27 | long_description = fh.read()
28 |
29 | setup(
30 | name='fontpreview',
31 | version=__version__,
32 | packages=['fontpreview'],
33 | url='https://github.com/matteoguadrini/fontpreview',
34 | license='GNU General Public License v3.0',
35 | author='Matteo Guadrini',
36 | author_email='matteo.guadrini@hotmail.it',
37 | keywords='fontpreview library font previews',
38 | maintainer='Matteo Guadrini',
39 | maintainer_email='matteo.guadrini@hotmail.it',
40 | install_requires=['Pillow'],
41 | description='Python library for font previews',
42 | long_description=long_description,
43 | long_description_content_type="text/markdown",
44 | classifiers=[
45 | "Programming Language :: Python :: 3",
46 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
47 | "Operating System :: OS Independent",
48 | ],
49 | scripts=['bin/fp'],
50 | python_requires='>=3.6'
51 | )
52 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 | import os
14 | import sys
15 |
16 | sys.path.insert(0, os.path.abspath('../..'))
17 |
18 | from fontpreview import __version__
19 |
20 | # -- Project information -----------------------------------------------------
21 |
22 | project = 'fontpreview'
23 | copyright = '2020, Matteo Guadrini'
24 | author = 'Matteo Guadrini'
25 |
26 | # -- General configuration ---------------------------------------------------
27 |
28 | # Add any Sphinx extension module names here, as strings. They can be
29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
30 | # ones.
31 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest']
32 |
33 | # Add any paths that contain templates here, relative to this directory.
34 | templates_path = ['_templates']
35 |
36 | # The full version, including alpha/beta/rc tags
37 | release = __version__
38 |
39 | # List of patterns, relative to source directory, that match files and
40 | # directories to ignore when looking for source files.
41 | # This pattern also affects html_static_path and html_extra_path.
42 | exclude_patterns = []
43 |
44 | # -- Options for HTML output -------------------------------------------------
45 |
46 | # The theme to use for HTML and HTML Help pages. See the documentation for
47 | # a list of builtin themes.
48 | #
49 | html_theme = "sphinx_rtd_theme"
50 |
51 | # Add any paths that contain custom static files (such as style sheets) here,
52 | # relative to this directory. They are copied after the builtin static files,
53 | # so a file named "default.css" will overwrite the builtin "default.css".
54 | html_static_path = ['_static']
55 |
56 | html_theme_options = {
57 | 'logo_only': False
58 | }
59 | html_logo = "_static/fp.png"
60 |
61 | master_doc = 'index'
62 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of this project, and in the interest of
4 | fostering an open and welcoming community, we pledge to respect all people who
5 | contribute through reporting issues, posting feature requests, updating
6 | documentation, submitting pull requests or patches, and other activities.
7 |
8 | We are committed to making participation in this project a harassment-free
9 | experience for everyone, regardless of level of experience, gender, gender
10 | identity and expression, sexual orientation, disability, personal appearance,
11 | body size, race, ethnicity, age, religion, or nationality.
12 |
13 | Examples of unacceptable behavior by participants include:
14 |
15 | * The use of sexualized language or imagery
16 | * Personal attacks
17 | * Trolling or insulting/derogatory comments
18 | * Public or private harassment
19 | * Publishing other's private information, such as physical or electronic
20 | addresses, without explicit permission
21 | * Other unethical or unprofessional conduct
22 |
23 | Project maintainers have the right and responsibility to remove, edit, or
24 | reject comments, commits, code, wiki edits, issues, and other contributions
25 | that are not aligned to this Code of Conduct, or to ban temporarily or
26 | permanently any contributor for other behaviors that they deem inappropriate,
27 | threatening, offensive, or harmful.
28 |
29 | By adopting this Code of Conduct, project maintainers commit themselves to
30 | fairly and consistently applying these principles to every aspect of managing
31 | this project. Project maintainers who do not follow or enforce the Code of
32 | Conduct may be permanently removed from the project team.
33 |
34 | This Code of Conduct applies both within project spaces and in public spaces
35 | when an individual is representing the project or its community.
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
38 | reported by contacting the project maintainers at msfdev@metasploit.com. If
39 | the incident involves a committer, you may report directly to
40 | egypt@metasploit.com or todb@metasploit.com.
41 |
42 | All complaints will be reviewed and investigated and will result in a
43 | response that is deemed necessary and appropriate to the circumstances.
44 | Maintainers are obligated to maintain confidentiality with regard to the
45 | reporter of an incident.
46 |
47 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
48 | version 1.3.0, available at
49 | [http://contributor-covenant.org/version/1/3/0/][version]
50 |
51 | [homepage]: http://contributor-covenant.org
52 | [version]: http://contributor-covenant.org/version/1/3/0/
--------------------------------------------------------------------------------
/docs/source/cli.rst:
--------------------------------------------------------------------------------
1 | Command line
2 | ############
3 |
4 | Here we explain how to use the *fontpreview* tool on the command line
5 |
6 | .. note::
7 | If you want to use the command line tool, you need to install the system-wide library: ``pip install fontpreview``
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 | :caption: Contents:
12 |
13 | This is help system:
14 |
15 | .. code-block:: console
16 |
17 | $ fp --help
18 | usage: fp [-h] [--verbose] [--version] [-t TEXT] [-b BG_COLOR] [-f FG_COLOR] [-i IMAGE]
19 | [-d DIMENSION DIMENSION] [-s SAVE_PATH] [-p TEXT_POSITION] [-z SIZE]
20 | font
21 |
22 | FontPreview cli
23 |
24 | positional arguments:
25 | font font file path
26 |
27 | optional arguments:
28 | -h, --help show this help message and exit
29 | --verbose, -v enable verbosity, for debug
30 | --version, -V show program's version number and exit
31 | -t TEXT, --text TEXT text include to preview image (default: a b c d e f)
32 | -b BG_COLOR, --background BG_COLOR
33 | background color (default: white)
34 | -f FG_COLOR, --foreground FG_COLOR
35 | foreground color (default: black)
36 | -i IMAGE, --background-image IMAGE
37 | background image path
38 | -d DIMENSION DIMENSION, --dimension DIMENSION DIMENSION
39 | dimension x and y (default: 700x327)
40 | -s SAVE_PATH, --save SAVE_PATH
41 | save file path (default: current directory)
42 | -p TEXT_POSITION, --text-position TEXT_POSITION
43 | save file path (default: center)
44 | -z SIZE, --size SIZE size of font (default: 64)
45 |
46 |
47 | Simple usage
48 | ************
49 |
50 | Save *fontpreview* image in a current directory from font file:
51 |
52 | .. code-block:: console
53 |
54 | $ fp /tmp/noto.ttf
55 |
56 | .. image:: https://i.ibb.co/258dCPZ/fp.png
57 | :alt: FontPreview image
58 |
59 | Advanced usage
60 | **************
61 |
62 | Use ``-v`` for debugging; ``-d`` setting dimension with **x** and **y** axis; ``-b`` setting background colors,
63 | ``-f`` setting foreground colors, ``-p`` setting text position, ``-z`` setting font size and ``-s`` specified file path to save.
64 |
65 | For the color reference: `colors `_
66 |
67 | .. code-block:: console
68 |
69 | $ fp /tmp/noto.ttf -v -t 'Hello Noto' -d 1000 1000 -b 'green' -f 'blue' -p 'lcenter' -z 50 -s /tmp/fp.png
70 | DEBUG: set text position: "lcenter"
71 | DEBUG: font object => font_name:('Noto Sans', 'Regular'),font_size:50,text:Hello Noto,text_position:(0, 473),dimension:(1000, 1000)
72 |
73 | .. image:: https://i.ibb.co/SfSmX44/fp.png
74 | :alt: FontPreview image
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### PyCharm+all ###
2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
3 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
4 |
5 | # User-specific stuff
6 | .idea/**/workspace.xml
7 | .idea/**/tasks.xml
8 | .idea/**/usage.statistics.xml
9 | .idea/**/dictionaries
10 | .idea/**/shelf
11 |
12 | # Sensitive or high-churn files
13 | .idea/**/dataSources/
14 | .idea/**/dataSources.ids
15 | .idea/**/dataSources.local.xml
16 | .idea/**/sqlDataSources.xml
17 | .idea/**/dynamic.xml
18 | .idea/**/uiDesigner.xml
19 | .idea/**/dbnavigator.xml
20 |
21 | # Gradle
22 | .idea/**/gradle.xml
23 | .idea/**/libraries
24 |
25 | # Gradle and Maven with auto-import
26 | # When using Gradle or Maven with auto-import, you should exclude module files,
27 | # since they will be recreated, and may cause churn. Uncomment if using
28 | # auto-import.
29 | # .idea/modules.xml
30 | # .idea/*.iml
31 | # .idea/modules
32 |
33 | # CMake
34 | cmake-build-*/
35 |
36 | # Mongo Explorer plugin
37 | .idea/**/mongoSettings.xml
38 |
39 | # File-based project format
40 | *.iws
41 |
42 | # IntelliJ
43 | out/
44 |
45 | # mpeltonen/sbt-idea plugin
46 | .idea_modules/
47 |
48 | # JIRA plugin
49 | atlassian-ide-plugin.xml
50 |
51 | # Cursive Clojure plugin
52 | .idea/replstate.xml
53 |
54 | # Crashlytics plugin (for Android Studio and IntelliJ)
55 | com_crashlytics_export_strings.xml
56 | crashlytics.properties
57 | crashlytics-build.properties
58 | fabric.properties
59 |
60 | # Editor-based Rest Client
61 | .idea/httpRequests
62 |
63 | ### PyCharm+all Patch ###
64 | # Ignores the whole .idea folder and all .iml files
65 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
66 |
67 | .idea/
68 |
69 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
70 |
71 | *.iml
72 | modules.xml
73 | .idea/misc.xml
74 | *.ipr
75 |
76 | ### Python ###
77 | # Byte-compiled / optimized / DLL files
78 | __pycache__/
79 | *.py[cod]
80 | *$py.class
81 |
82 | # C extensions
83 | *.so
84 |
85 | # Distribution / packaging
86 | .Python
87 | build/
88 | develop-eggs/
89 | dist/
90 | downloads/
91 | eggs/
92 | .eggs/
93 | lib/
94 | lib64/
95 | parts/
96 | sdist/
97 | var/
98 | wheels/
99 | *.egg-info/
100 | .installed.cfg
101 | *.egg
102 | MANIFEST
103 |
104 | # PyInstaller
105 | # Usually these files are written by a python script from a template
106 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
107 | *.manifest
108 | *.spec
109 |
110 | # Installer logs
111 | pip-log.txt
112 | pip-delete-this-directory.txt
113 |
114 | # Unit test / coverage reports
115 | htmlcov/
116 | .tox/
117 | .coverage
118 | .coverage.*
119 | .cache
120 | nosetests.xml
121 | coverage.xml
122 | *.cover
123 | .hypothesis/
124 | .pytest_cache/
125 |
126 | # Translations
127 | *.mo
128 | *.pot
129 |
130 | # Django stuff:
131 | *.log
132 | local_settings.py
133 | db.sqlite3
134 |
135 | # Flask stuff:
136 | instance/
137 | .webassets-cache
138 |
139 | # Scrapy stuff:
140 | .scrapy
141 |
142 | # Sphinx documentation
143 | docs/_build/
144 |
145 | # PyBuilder
146 | target/
147 |
148 | # Jupyter Notebook
149 | .ipynb_checkpoints
150 |
151 | # pyenv
152 | .python-version
153 |
154 | # celery beat schedule file
155 | celerybeat-schedule
156 |
157 | # SageMath parsed files
158 | *.sage.py
159 |
160 | # Environments
161 | .env
162 | .venv
163 | env/
164 | venv/
165 | ENV/
166 | env.bak/
167 | venv.bak/
168 |
169 | # Spyder project settings
170 | .spyderproject
171 | .spyproject
172 |
173 | # Rope project settings
174 | .ropeproject
175 |
176 | # mkdocs documentation
177 | /site
178 |
179 | # mypy
180 | .mypy_cache/
181 |
182 | ### Python Patch ###
183 | .venv/
184 |
185 | ### Python.VirtualEnv Stack ###
186 | # Virtualenv
187 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
188 | [Ii]nclude
189 | [Ll]ib
190 | [Ll]ib64
191 | [Ll]ocal
192 | [Ss]cripts
193 | pyvenv.cfg
194 | pip-selfcheck.json
195 |
196 | ### VisualStudioCode ###
197 | .vscode/*
198 | !.vscode/settings.json
199 | !.vscode/tasks.json
200 | !.vscode/launch.json
201 | !.vscode/extensions.json
202 |
203 |
204 | # End of https://www.gitignore.io/api/python,pycharm+all,visualstudiocode
205 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | When contributing to this repository, please first discuss the change you wish to make via issue,
4 | email, or any other method with the owners of this repository before making a change.
5 |
6 | Please note we have a code of conduct, please follow it in all your interactions with the project.
7 |
8 | ## Pull Request Process
9 |
10 | 1. Be sure to use the Pull Request templates and its guidelines contained therein.
11 | 2. Update the README.md with details of changes.
12 | 3. Increase the version numbers in variable and manual to the new version that this
13 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
14 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
15 | do not have permission to do that, you may request the second reviewer to merge it for you.
16 |
17 | ## Code of Conduct
18 |
19 | ### Our Pledge
20 |
21 | In the interest of fostering an open and welcoming environment, we as
22 | contributors and maintainers pledge to making participation in our project and
23 | our community a harassment-free experience for everyone, regardless of age, body
24 | size, disability, ethnicity, gender identity and expression, level of experience,
25 | nationality, personal appearance, race, religion, or sexual identity and
26 | orientation.
27 |
28 | ### Our Standards
29 |
30 | Examples of behavior that contributes to creating a positive environment
31 | include:
32 |
33 | * Using welcoming and inclusive language
34 | * Being respectful of differing viewpoints and experiences
35 | * Gracefully accepting constructive criticism
36 | * Focusing on what is best for the community
37 | * Showing empathy towards other community members
38 |
39 | Examples of unacceptable behavior by participants include:
40 |
41 | * The use of sexualized language or imagery and unwelcome sexual attention or
42 | advances
43 | * Trolling, insulting/derogatory comments, and personal or political attacks
44 | * Public or private harassment
45 | * Publishing others' private information, such as a physical or electronic
46 | address, without explicit permission
47 | * Other conduct which could reasonably be considered inappropriate in a
48 | professional setting
49 |
50 | ### Our Responsibilities
51 |
52 | Project maintainers are responsible for clarifying the standards of acceptable
53 | behavior and are expected to take appropriate and fair corrective action in
54 | response to any instances of unacceptable behavior.
55 |
56 | Project maintainers have the right and responsibility to remove, edit, or
57 | reject comments, commits, code, wiki edits, issues, and other contributions
58 | that are not aligned to this Code of Conduct, or to ban temporarily or
59 | permanently any contributor for other behaviors that they deem inappropriate,
60 | threatening, offensive, or harmful.
61 |
62 | ### Scope
63 |
64 | This Code of Conduct applies both within project spaces and in public spaces
65 | when an individual is representing the project or its community. Examples of
66 | representing a project or community include using an official project e-mail
67 | address, posting via an official social media account, or acting as an appointed
68 | representative at an online or offline event. Representation of a project may be
69 | further defined and clarified by project maintainers.
70 |
71 | ### Enforcement
72 |
73 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
74 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
75 | complaints will be reviewed and investigated and will result in a response that
76 | is deemed necessary and appropriate to the circumstances. The project team is
77 | obligated to maintain confidentiality with regard to the reporter of an incident.
78 | Further details of specific enforcement policies may be posted separately.
79 |
80 | Project maintainers who do not follow or enforce the Code of Conduct in good
81 | faith may face temporary or permanent repercussions as determined by other
82 | members of the project's leadership.
83 |
84 | ### Attribution
85 |
86 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
87 | available at [http://contributor-covenant.org/version/1/4][version]
88 |
89 | [homepage]: http://contributor-covenant.org
90 | [version]: http://contributor-covenant.org/version/1/4/
--------------------------------------------------------------------------------
/bin/fp:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- encoding: utf-8 -*-
3 | # vim: se ts=4 et syn=python:
4 |
5 | # created by: matteo.guadrini
6 | # fp -- fontpreview
7 | #
8 | # Copyright (C) 2020 Matteo Guadrini
9 | #
10 | # This program is free software: you can redistribute it and/or modify
11 | # it under the terms of the GNU General Public License as published by
12 | # the Free Software Foundation, either version 3 of the License, or
13 | # (at your option) any later version.
14 | #
15 | # This program is distributed in the hope that it will be useful,
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | # GNU General Public License for more details.
19 | #
20 | # You should have received a copy of the GNU General Public License
21 | # along with this program. If not, see .
22 |
23 | from argparse import ArgumentParser
24 | from fontpreview import FontPreview
25 | from fontpreview import __version__
26 |
27 |
28 | def parse_arguments():
29 | """
30 | Function that captures the parameters and the arguments in the command line
31 |
32 | :return: parser object
33 | """
34 | # Create a common parser
35 | common_parser = ArgumentParser(add_help=False)
36 | common_parser.add_argument('--verbose', '-v', help='enable verbosity, for debug',
37 | dest='verbose', action='store_true')
38 |
39 | # Create a principal parser
40 | parser_object = ArgumentParser(prog='fp', description='FontPreview cli', parents=[common_parser])
41 | parser_object.add_argument('--version', '-V', action='version', version='%(prog)s ' + __version__)
42 | parser_object.add_argument('font', help='font file path')
43 | parser_object.add_argument('-t', '--text', help='text include to preview image (default: a b c d e f)', dest='text')
44 | parser_object.add_argument('-b', '--background', help='background color (default: white)', dest='bg_color')
45 | parser_object.add_argument('-f', '--foreground', help='foreground color (default: black)', dest='fg_color')
46 | parser_object.add_argument('-i', '--background-image', help='background image path', dest='image')
47 | parser_object.add_argument('-d', '--dimension', help='dimension x and y (default: 700x327)', nargs=2,
48 | dest='dimension', type=int)
49 | parser_object.add_argument('-s', '--save', help='save file path (default: current directory)', dest='save_path')
50 | parser_object.add_argument('-p', '--text-position', help='save file path (default: center)',
51 | dest='text_position')
52 | parser_object.add_argument('-z', '--size', help='size of font (default: 64)', dest='size', type=int)
53 |
54 | # Return parser object
55 | return parser_object
56 |
57 |
58 | def fp_arguments(arguments):
59 | """
60 | Check arguments and build dictionary arguments for FontPreview class
61 |
62 | :param arguments: argparse arguments
63 | :return: dictionary
64 | """
65 | # Initialize dictionary arguments
66 | fp_args = {}
67 | # Check all args passed in command line
68 | if arguments.font:
69 | fp_args['font'] = arguments.font
70 | if arguments.text:
71 | fp_args['font_text'] = arguments.text
72 | if arguments.bg_color:
73 | fp_args['bg_color'] = arguments.bg_color
74 | if arguments.fg_color:
75 | fp_args['fg_color'] = arguments.fg_color
76 | if arguments.image:
77 | fp_args['bg_image'] = arguments.image
78 | if arguments.dimension:
79 | fp_args['dimension'] = tuple(arguments.dimension)
80 | if arguments.size:
81 | fp_args['font_size'] = arguments.size
82 | # Return dictionary
83 | return fp_args
84 |
85 |
86 | def v_print(verbose, *message):
87 | """
88 | Format verbosity message
89 |
90 | :param verbose: verbosity boolean
91 | :param message: list of message to print in verbosity mode
92 | :return: verbosity message
93 | """
94 | if verbose:
95 | print('DEBUG:', *message)
96 |
97 |
98 | if __name__ == '__main__':
99 | # Parse arguments
100 | option = parse_arguments()
101 | args = option.parse_args()
102 | fpargs = fp_arguments(args)
103 |
104 | # Initialize object
105 | fp = FontPreview(**fpargs)
106 |
107 | # Check other args
108 | if args.image:
109 | v_print(args.verbose, 'set background with image {0}'.format(args.image))
110 | fp.bg_image = args.image
111 | fp.draw()
112 | if args.text_position:
113 | v_print(args.verbose, 'set text position: "{0}"'.format(args.text_position))
114 | fp.set_text_position(args.text_position)
115 |
116 | # Save image
117 | if args.save_path:
118 | fp.save(args.save_path)
119 | else:
120 | fp.save()
121 |
122 | v_print(args.verbose, 'font object => {0}'.format(fp))
123 |
--------------------------------------------------------------------------------
/fontpreview/fontpreview.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- encoding: utf-8 -*-
3 | # vim: se ts=4 et syn=python:
4 |
5 | # created by: matteo.guadrini
6 | # fontpreview -- fontpreview
7 | #
8 | # Copyright (C) 2020 Matteo Guadrini
9 | #
10 | # This program is free software: you can redistribute it and/or modify
11 | # it under the terms of the GNU General Public License as published by
12 | # the Free Software Foundation, either version 3 of the License, or
13 | # (at your option) any later version.
14 | #
15 | # This program is distributed in the hope that it will be useful,
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | # GNU General Public License for more details.
19 | #
20 | # You should have received a copy of the GNU General Public License
21 | # along with this program. If not, see .
22 |
23 | # region imports
24 | import os
25 | from PIL import Image, ImageDraw, ImageFont
26 |
27 | # endregion
28 |
29 | # region variable
30 | CALC_POSITION = {
31 | 'center': lambda ixy, fxy: ((ixy[0] - fxy[0]) // 2, (ixy[1] - fxy[1]) // 2),
32 | 'top': lambda ixy, fxy: ((ixy[0] - fxy[0]) // 2, 0),
33 | 'below': lambda ixy, fxy: ((ixy[0] - fxy[0]) // 2, (ixy[1] - fxy[1])),
34 | 'rcenter': lambda ixy, fxy: ((ixy[0] - fxy[0]), (ixy[1] - fxy[1]) // 2),
35 | 'rtop': lambda ixy, fxy: ((ixy[0] - fxy[0]), 0),
36 | 'rbelow': lambda ixy, fxy: ((ixy[0] - fxy[0]), (ixy[1] - fxy[1])),
37 | 'lcenter': lambda ixy, fxy: (0, (ixy[1] - fxy[1]) // 2),
38 | 'ltop': lambda ixy, fxy: (0, 0),
39 | 'lbelow': lambda ixy, fxy: (0, (ixy[1] - fxy[1])),
40 | }
41 |
42 |
43 | # endregion
44 |
45 | # region classes
46 | class FontPreview:
47 | """
48 | Class that represents the preview of a font
49 | """
50 |
51 | def __init__(self, font,
52 | font_size=64,
53 | font_text='a b c d e f',
54 | color_system='RGB',
55 | bg_color='white',
56 | fg_color='black',
57 | dimension=(700, 327)
58 | ):
59 | """
60 | Object that represents the preview of a font
61 |
62 | :param font: font file
63 | :param font_size: font size. Default is 64.
64 | :param font_text: font text representation. Default is 'a b c d e f'.
65 | :param color_system: color system string. Default is 'RGB'.
66 | :param bg_color: background color of preview. Default is 'white'.
67 | :param fg_color: foreground or font color of preview. Default is 'black'.
68 | :param dimension: dimension of preview. Default is 700x327.
69 | """
70 | # Define properties
71 | self.image = None
72 | self.font_size = font_size
73 | self.font_text = font_text
74 | self.font = ImageFont.truetype(font=font, size=self.font_size)
75 | self.color_system = color_system
76 | self.bg_image = None
77 | self.bg_color = bg_color
78 | self.fg_color = fg_color
79 | self.dimension = dimension
80 | self.font_position = CALC_POSITION['center'](self.dimension, self.font.getsize(self.font_text))
81 | # Create default image
82 | self.draw()
83 |
84 | def __str__(self):
85 | """
86 | String representation of font preview
87 |
88 | :return: string
89 | """
90 | return "font_name:{font},font_size:{size},text:{text},text_position:{position},dimension:{dimension}".format(
91 | font=self.font.getname(), size=self.font_size, text=self.font_text,
92 | position=self.font_position, dimension=self.dimension
93 | )
94 |
95 | def __resize(self):
96 | """
97 | Resize the font if it exceeds the size of the background
98 |
99 | :return: None
100 | """
101 | img = ImageDraw.Draw(self.image)
102 | # Check font size
103 | text_size = img.multiline_textsize(self.font_text, self.font)
104 | while text_size > self.dimension:
105 | self.font_size = self.font_size - 2
106 | self.font = ImageFont.truetype(font=self.font.path, size=self.font_size)
107 | text_size = img.multiline_textsize(self.font_text, self.font)
108 |
109 | def save(self, path=os.path.join(os.path.abspath(os.getcwd()), 'fontpreview.png')):
110 | """
111 | Save the preview font
112 |
113 | :param path: path where you want to save the preview font
114 | :return: None
115 | """
116 | self.image.save(path)
117 |
118 | def draw(self, align='left'):
119 | """
120 | Draw image with text based on properties of this object
121 |
122 | :param align: alignment of text. Available 'left', 'center' and 'right'
123 | :return: None
124 | """
125 | # Set an image
126 | if self.bg_image:
127 | self.image = Image.open(self.bg_image)
128 | # Draw background with flat color
129 | else:
130 | self.image = Image.new(self.color_system, self.dimension, color=self.bg_color)
131 | draw = ImageDraw.Draw(self.image)
132 | draw.text(self.font_position, self.font_text, fill=self.fg_color, font=self.font, align=align)
133 |
134 | def show(self):
135 | """
136 | Displays this image.
137 |
138 | :return: None
139 | """
140 | self.image.show()
141 |
142 | def set_font_size(self, size):
143 | """
144 | Set size of font
145 |
146 | :param size: size of font
147 | :return: None
148 | """
149 | # Set size of font
150 | self.font_size = size
151 | self.font = ImageFont.truetype(font=self.font.path, size=self.font_size)
152 | self.__resize()
153 | # Create image
154 | self.draw()
155 |
156 | def set_text_position(self, position):
157 | """
158 | Set position of text
159 |
160 | :param position: Position can be a tuple with x and y axis, or a string.
161 | The strings available are 'center', 'top', 'below', 'rcenter', 'rtop', 'rbelow', 'lcenter', 'ltop'
162 | and 'lbelow'.
163 |
164 | :return: None
165 | """
166 | # Create image drawer
167 | img = ImageDraw.Draw(self.image)
168 | if isinstance(position, tuple):
169 | self.font_position = position
170 | else:
171 | self.font_position = CALC_POSITION.get(position, CALC_POSITION['center'])(
172 | self.dimension, img.multiline_textsize(self.font_text, self.font)
173 | )
174 | # Create image
175 | self.draw()
176 |
177 | # endregion
178 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | [](https://www.codacy.com/gh/MatteoGuadrini/fontpreview/dashboard?utm_source=github.com&utm_medium=referral&utm_content=MatteoGuadrini/fontpreview&utm_campaign=Badge_Grade)
5 | [](https://circleci.com/gh/MatteoGuadrini/fontpreview)
6 |
7 | # fontpreview: Python library for font previews
8 |
9 | This is a library that allows you to create preview images from one or more selected fonts.
10 |
11 | Full docs is here: [ReadTheDocs](https://fontpreview.readthedocs.io/en/latest/)
12 |
13 | ## Test
14 |
15 | If you want to test the package before installing, use the test:
16 | ```console
17 | $ git clone https://github.com/MatteoGuadrini/fontpreview.git
18 | $ cd fontpreview
19 | $ python -m unittest test_fp.py
20 | ```
21 |
22 | ## Installation
23 |
24 | Use Pypi:
25 | ```console
26 | $ pip install --user fontpreview
27 | ```
28 |
29 | > **Note**: If you want to use the command line tool, you need to install the system-wide library: `pip install fontpreview`
30 |
31 | ## Simple usage
32 |
33 | Preview example:
34 | ```python
35 | from fontpreview import FontPreview
36 |
37 | fp = FontPreview('/tmp/noto.ttf')
38 | fp.save('/tmp/fp.png')
39 | ```
40 |
41 |
42 |
43 | Banner example:
44 | ```python
45 | from fontpreview import FontBanner
46 |
47 | fb = FontBanner('/tmp/noto.ttf', 'landscape', bg_color=(153, 153, 255), mode='fontname')
48 | fb.save('/tmp/fb.png')
49 | ```
50 |
51 |
52 |
53 | Logo example:
54 | ```python
55 | from fontpreview import FontLogo
56 |
57 | fl = FontLogo('/tmp/noto.ttf', 'Fp')
58 | fl.save('/tmp/fl.png')
59 | ```
60 |
61 |
62 |
63 | Font wall example:
64 | ```python
65 | from fontpreview import FontBanner, FontWall
66 |
67 | # Define the various parts of wall
68 | fb = FontBanner('/tmp/noto.ttf', 'landscape' , mode='fontname')
69 | fb2 = FontBanner('/tmp/noto.ttf', 'landscape' , mode='alpha')
70 | fb3 = FontBanner('/tmp/noto.ttf', 'landscape' , mode='letter')
71 | fb4 = FontBanner('/tmp/noto.ttf', 'landscape' , mode='paragraph')
72 | fw = FontWall([fb,fb2,fb3,fb4])
73 | fw.save('/tmp/fw.png')
74 | ```
75 |
76 |
77 |
78 |
79 | Font page example:
80 | ```python
81 | from fontpreview import FontPage, FontBanner
82 |
83 | # Define the various parts of page
84 | header = FontBanner('/tmp/noto.ttf', 'landscape' , mode='fontname')
85 | body = FontBanner('/tmp/noto.ttf', 'landscape' , mode='paragraph')
86 | footer = FontBanner('/tmp/noto.ttf', 'landscape' , mode='letter')
87 | # Create FontPage object
88 | fpage = FontPage()
89 | fpage.set_header(header)
90 | fpage.set_body(body)
91 | fpage.set_footer(footer)
92 | # Design all parts
93 | fpage.draw()
94 | fpage.save('/tmp/fpage.png')
95 |
96 | ```
97 |
98 |
99 |
100 | Font page with template example:
101 | ```python
102 | from fontpreview import FontPage, FontPageTemplate, FontBanner
103 |
104 | # Define the various parts of page
105 | header = FontBanner('/tmp/noto.ttf', 'landscape' , mode='fontname')
106 | body = FontBanner('/tmp/noto.ttf', 'landscape' , mode='paragraph')
107 | footer = FontBanner('/tmp/noto.ttf', 'landscape' , mode='letter')
108 | # Create font page template
109 | template = FontPageTemplate(3508)
110 | template.set_body(170, 1, 'lcenter')
111 | template.set_footer(100, 4, 'lcenter')
112 | # Create FontPage object
113 | fpage = FontPage(template=template)
114 | fpage.set_header(header)
115 | fpage.set_body(body)
116 | fpage.set_footer(footer)
117 | # Design all parts
118 | fpage.draw()
119 | fpage.save('/tmp/fpage_template.png')
120 |
121 | ```
122 |
123 |
124 |
125 | Font booklet example:
126 | ```python
127 | from fontpreview import FontPage, FontBanner, FontBooklet
128 |
129 | # Define the various parts of page
130 | header = FontBanner('/tmp/noto.ttf', 'landscape' , mode='fontname')
131 | body = FontBanner('/tmp/noto.ttf', 'landscape' , mode='paragraph')
132 | footer = FontBanner('/tmp/noto.ttf', 'landscape' , mode='letter')
133 | # Create FontPage object
134 | fpage1 = FontPage(header=header, body=body, footer=footer)
135 | fpage2 = FontPage(header=header, body=body, footer=footer)
136 | # Design all parts
137 | fpage1.draw()
138 | fpage2.draw()
139 | # Create book
140 | book = FontBooklet(fpage1, fpage2)
141 | book.save('/tmp/noto_book/') # save page1.png, page2.png in /tmp/noto_book/ folder
142 |
143 | ```
144 |
145 | ## Advanced usage
146 |
147 | Below is an example of various previews of the _"Fira Code regular"_ font.
148 | Does it remind you of anything? [Fira code original](https://github.com/tonsky/FiraCode/raw/master/extras/logo.svg)
149 | ```python
150 | # FIRA CODE WALL
151 | from fontpreview import FontBanner, FontWall
152 | fira_code = '/tmp/firacode.ttf'
153 | # RGB group = ('background', 'FIRA COD color', 'Ligature color', 'E color background')
154 | colors_group = [
155 | ('black', (0, 143, 0), (0, 236, 236), (255, 0, 255)),
156 | ('black', (166, 47, 123), (81, 208, 93), (11, 179, 248)),
157 | ((13, 21, 43), (112, 204, 84), (226, 110, 34), (223, 245, 90)),
158 | ((43, 6, 42), (136, 126, 135), (4, 150, 153), (147, 103, 145)),
159 | ((39, 57, 85), (255, 241, 208), (208, 84, 0), (209, 215, 227)),
160 | ((31, 63, 89), (248, 248, 242), (230, 219, 117), (166, 226, 51)),
161 | ((1, 47, 80), (224, 202, 52), (73, 217, 38), (255, 125, 158)),
162 | ((0, 0, 170), (75, 224, 245), (255, 255, 85), (0, 170, 170)),
163 | ('white', 'black', 'black', 'black'),
164 | ((247, 247, 247), (167, 29, 93), (121, 93, 163), (0, 134, 179)),
165 | ((239, 240, 243), (15, 131, 207), (208, 84, 0), (105, 40, 122)),
166 | ((239, 231, 212), (218, 116, 53), (0, 142, 212), (186, 136, 0)),
167 | ((39, 40, 34), (132, 214, 45), (249, 39, 114), (174, 129, 255)),
168 | ((43, 48, 59), (180, 142, 173), (143, 161, 179), (152, 190, 140)),
169 | ((32, 32, 32), (171, 130, 84), (160, 171, 127), (216, 127, 98)),
170 | ((0, 43, 54), (0, 160, 153), (126, 143, 3), (218, 66, 130))
171 | ]
172 | banners = []
173 | # Create banners
174 | for colors in colors_group:
175 | # Create a FontBanner objects
176 | fb = FontBanner(fira_code, (413, 240))
177 | liga = FontBanner(fira_code, (413, 240))
178 | E = FontBanner(fira_code, (40, 70))
179 | # Set background colors
180 | fb.bg_color = liga.bg_color = colors[0]
181 | E.bg_color = colors[3]
182 | # Set foreground colors
183 | fb.fg_color = colors[1]
184 | liga.fg_color = colors[2]
185 | E.fg_color = colors[0]
186 | # Set text
187 | fb.font_text = 'FIRA COD'
188 | liga.font_text = "!=->>++:="
189 | E.font_text = 'E'
190 | # Set text position
191 | E.set_text_position('ltop')
192 | fb.set_text_position((25, 60))
193 | liga.set_text_position('top')
194 | # Adding image on fb
195 | fb.add_image(liga, (0, 122))
196 | fb.add_image(E, (339, 60))
197 | # Add to list of banners
198 | banners.append(fb)
199 |
200 | # Create a wall
201 | fw = FontWall(banners, max_tile=4)
202 | fw.save('/tmp/fira_code.png')
203 | ```
204 |
205 |
206 |
207 | ## Command line interface
208 | Along with the package, a command line tool based on this python package is installed.
209 | The class used for command line previews is FontPreview. For all the options of this tool,
210 | see the [docs](https://fontpreview.readthedocs.io/en/latest/cli.html), otherwise run `fp -h`.
211 |
212 | ```console
213 | $ fp /tmp/noto.ttf
214 | ```
215 |
216 | This command save a *fontpreview.png* in a current directory.
217 |
218 |
219 |
220 |
221 | ## Open source
222 | _fontpreview_ is a open source project. Any contribute, It's welcome.
223 |
224 | **A great thanks**.
225 |
226 | For donations, press this
227 |
228 | For me
229 |
230 | [](https://www.paypal.me/guos)
231 |
232 | For [Telethon](http://www.telethon.it/)
233 |
234 | The Telethon Foundation is a non-profit organization recognized by the Ministry of University and Scientific and Technological Research.
235 | They were born in 1990 to respond to the appeal of patients suffering from rare diseases.
236 | Come today, we are organized to dare to listen to them and answers, every day of the year.
237 |
238 |
239 |
240 | [Adopt the future](https://www.ioadottoilfuturo.it/)
241 |
242 |
243 | ## Acknowledgments
244 |
245 | Thanks to Mark Lutz for writing the _Learning Python_ and _Programming Python_ books that make up my python foundation.
246 |
247 | Thanks to Kenneth Reitz and Tanya Schlusser for writing the _The Hitchhiker’s Guide to Python_ books.
248 |
249 | Thanks to Dane Hillard for writing the _Practices of the Python Pro_ books.
250 |
251 | Special thanks go to my wife, who understood the hours of absence for this development.
252 | Thanks to my children, for the daily inspiration they give me and to make me realize, that life must be simple.
253 |
254 | Thanks Python!
255 |
--------------------------------------------------------------------------------
/test_fp.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import os
3 | from fontpreview import FontPreview, FontBanner, FontLogo, FontWall, FontPage, FontPageTemplate, FontBooklet
4 |
5 | # Enter the file font to test and check exists
6 | font = input('Enter path of font file to test: ')
7 | if not os.path.exists(font):
8 | raise OSError("file font {0} doesn't exists".format(font))
9 |
10 |
11 | class TestFontPreview(unittest.TestCase):
12 | fp, fb, fl = FontPreview(font), FontBanner(font), FontLogo(font, 'Fl')
13 | fw, fpage, fpage_t = FontWall([fb]), FontPage(), FontPageTemplate(3508)
14 | book = FontBooklet(fpage, fpage)
15 |
16 | def test_instance(self):
17 | # test if instance has been created
18 | self.assertIsInstance(self.fp, FontPreview)
19 | self.assertIsInstance(self.fb, FontBanner)
20 | self.assertIsInstance(self.fl, FontLogo)
21 | self.assertIsInstance(self.fw, FontWall)
22 | self.assertIsInstance(self.fpage, FontPage)
23 | self.assertIsInstance(self.fpage_t, FontPageTemplate)
24 | self.assertIsInstance(self.book, FontBooklet)
25 |
26 | def test_set_color_with_name(self):
27 | # change background color
28 | self.fp.bg_color = self.fb.bg_color = self.fl.bg_color = 'blue'
29 | # change background color
30 | self.fp.fg_color = self.fb.fg_color = self.fl.fg_color = 'yellow'
31 | # test draw it
32 | self.fp.draw()
33 | self.fb.draw()
34 | self.fl.draw()
35 |
36 | def test_set_color_with_tuple(self):
37 | # change background color
38 | self.fp.bg_color = self.fb.bg_color = self.fl.bg_color = (51, 153, 193)
39 | # change background color
40 | self.fp.fg_color = self.fb.fg_color = self.fl.fg_color = (253, 194, 45)
41 | # test draw it
42 | self.fp.draw()
43 | self.fb.draw()
44 | self.fl.draw()
45 |
46 | def test_set_dimension(self):
47 | # test dimension of FontPreview object
48 | self.fp.dimension = (1000, 1000)
49 | # test draw it
50 | self.fp.draw()
51 | self.assertEqual(self.fp.image.size, self.fp.dimension)
52 | # test dimension of FontBanner object
53 | self.fb.set_orientation((1000, 1000))
54 | # test draw it
55 | self.fb.draw()
56 | self.assertEqual(self.fb.image.size, self.fb.dimension)
57 | # test dimension of FontBanner object and font position
58 | self.fb.set_orientation('landscape', 'lcenter')
59 | # test draw it
60 | self.fb.draw()
61 | self.assertEqual(self.fb.image.size, self.fb.dimension)
62 | # test dimension of FontLogo object
63 | self.fl.new_size((75, 75))
64 | self.fl.new_size((100, 100))
65 | self.fl.new_size((150, 150))
66 | self.fl.new_size((170, 170))
67 | # test draw it
68 | self.fp.draw()
69 | self.assertEqual(self.fl.image.size, self.fl.dimension)
70 | # test dimension of FontPage
71 | self.fpage.set_header(self.fb)
72 | self.fpage.set_body(self.fb)
73 | self.fpage.set_footer(self.fb)
74 | self.fpage.dimension = (1000, 1000)
75 | # test draw it
76 | self.fpage.draw()
77 | self.assertEqual(self.fpage.page.size, self.fpage.dimension)
78 |
79 | def test_add_image(self):
80 | # test add image to FontBanner
81 | self.fb.add_image(self.fp, (500, 500))
82 | # test resize in add image
83 | fb_big = FontBanner(font, (2000, 2000))
84 | self.fb.add_image(fb_big, (500, 500))
85 |
86 | def test_font_size(self):
87 | # Test FontPreview font size
88 | self.fp.set_font_size(70)
89 | self.assertEqual(self.fp.font.size, 70)
90 | # Test FontBanner font size
91 | self.fb.set_font_size(80)
92 | self.assertEqual(self.fb.font.size, 80)
93 | # Test FontBanner font size
94 | self.fl.set_font_size(50)
95 | self.assertEqual(self.fl.font.size, 50)
96 | # Test FontPage and FontPageTemplate font size
97 | template = FontPageTemplate()
98 | template.set_header(90, 1, 'lcenter')
99 | template.set_body(90, 3, 'lcenter')
100 | template.set_footer(90, 2, 'lcenter')
101 | fpage = FontPage(template=template)
102 | fpage.set_header(self.fb)
103 | fpage.set_body(self.fb)
104 | fpage.set_footer(self.fb)
105 | fpage.draw()
106 | # FontPageTemplate font size
107 | self.assertEqual(template.header_font_size, 90)
108 | self.assertEqual(template.body_font_size, 90)
109 | self.assertEqual(template.footer_font_size, 90)
110 | # FontPage font size
111 | self.assertEqual(fpage.header.font.size, 90)
112 | self.assertEqual(fpage.body.font.size, 90)
113 | self.assertEqual(fpage.footer.font.size, 90)
114 |
115 | def test_text_position(self):
116 | # Test FontPreview font size
117 | self.fp.set_text_position('lcenter')
118 | # Test FontBanner font size
119 | self.fb.set_text_position('rcenter')
120 | # Test FontBanner font size
121 | self.fl.set_text_position('center')
122 | # Test FontPreview font size
123 | self.fp.set_text_position((100, 100))
124 | # Test FontBanner font size
125 | self.fb.set_text_position((100, 100))
126 | # Test FontBanner font size
127 | self.fl.set_text_position((100, 100))
128 |
129 | def test_set_text(self):
130 | # Test FontPreview font size
131 | self.fp.font_text = 'unittest'
132 | self.fp.draw(align='center')
133 | self.assertEqual(self.fp.font_text, 'unittest')
134 | # Test FontBanner font size
135 | self.fb.set_mode('fontname')
136 | self.fb.draw(align='center')
137 | self.assertEqual(self.fb.font_text, '{0}'.format(self.fb.font.getname()[0]))
138 | # Test FontBanner font size
139 | self.fl.font_text = 'ut'
140 | self.fl.draw(align='center')
141 | self.assertEqual(self.fl.font_text, 'ut')
142 |
143 | def test_add_to_wall(self):
144 | # Create FontWall
145 | fw = FontWall([self.fb, self.fp, self.fl])
146 | self.assertIsInstance(fw, FontWall)
147 | fw.draw(fw.max_tile)
148 | # Create FontWall with max_tile args
149 | fw = FontWall([self.fb, self.fp, self.fl], max_tile=3)
150 | self.assertIsInstance(fw, FontWall)
151 | fw.draw(fw.max_tile)
152 | # Create FontWall with max_tile args and
153 | fw = FontWall([self.fb, self.fp, self.fl], max_tile=3, mode='vertical')
154 | self.assertIsInstance(fw, FontWall)
155 | fw.draw(fw.max_tile)
156 |
157 | def test_template_page(self):
158 | # Create FontPage
159 | page = FontPage(template=self.fpage_t)
160 | # Test instance
161 | self.assertIsInstance(page, FontPage)
162 | # Test method FontPageTemplate
163 | self.fpage_t.set_header(120, 1, 'center')
164 | self.fpage_t.set_body(170, 3, 'left')
165 | self.fpage_t.set_footer(100, 2, 'right')
166 | # Test value of header
167 | self.assertEqual(self.fpage_t.header_text_position, 'center')
168 | self.assertEqual(self.fpage_t.header_font_size, 120)
169 | self.assertEqual(self.fpage_t.header_units, self.fpage_t.unit * 1)
170 | # Test value of header
171 | self.assertEqual(self.fpage_t.body_text_position, 'left')
172 | self.assertEqual(self.fpage_t.body_font_size, 170)
173 | self.assertEqual(self.fpage_t.body_units, self.fpage_t.unit * 3)
174 | # Test value of header
175 | self.assertEqual(self.fpage_t.footer_text_position, 'right')
176 | self.assertEqual(self.fpage_t.footer_font_size, 100)
177 | self.assertEqual(self.fpage_t.footer_units, self.fpage_t.unit * 2)
178 |
179 | def test_declarative_object(self):
180 | # FontPreview object
181 | fp = FontPreview(font,
182 | font_size=50,
183 | font_text='some text',
184 | color_system='RGB',
185 | bg_color='blue',
186 | fg_color='yellow',
187 | dimension=(800, 400))
188 | # FontBanner object
189 | fb = FontBanner(font,
190 | orientation='portrait',
191 | bg_color='blue',
192 | fg_color='yellow',
193 | mode='paragraph',
194 | font_size=70,
195 | color_system='RGB')
196 | # FontLogo object
197 | fl = FontLogo(font,
198 | 'Fl',
199 | size=(170, 170),
200 | bg_color='yellow',
201 | fg_color='blue',
202 | font_size=50,
203 | color_system='RGB')
204 | # FontPage object
205 | fpage = FontPage(header=fb, logo=fl, body=fb, footer=fb)
206 | # test if instance has been created
207 | self.assertIsInstance(fp, FontPreview)
208 | self.assertIsInstance(fb, FontBanner)
209 | self.assertIsInstance(fl, FontLogo)
210 | self.assertIsInstance(fpage, FontPage)
211 |
212 | def test_other_color_system(self):
213 | fp = self.fp
214 | fb = self.fb
215 | fl = self.fl
216 | # define new color system
217 | fp.color_system = 'CMYK'
218 | # change background color
219 | fp.bg_color = fb.bg_color = fl.bg_color = (51, 153, 193)
220 | # change background color
221 | fp.fg_color = fb.fg_color = fl.fg_color = (253, 194, 45)
222 | # test draw it
223 | fp.draw()
224 | fb.draw()
225 | fl.draw()
226 |
227 | def test_hex_color(self):
228 | fp = self.fp
229 | fb = self.fb
230 | fl = self.fl
231 | # define new color system
232 | fp.color_system = 'CMYK'
233 | # change background color
234 | fp.bg_color = fb.bg_color = fl.bg_color = "#269cc3"
235 | # change background color
236 | fp.fg_color = fb.fg_color = fl.fg_color = "#ff0000"
237 | # test draw it
238 | fp.draw()
239 | fb.draw()
240 | fl.draw()
241 |
242 | def test_parse_not_fontpage_on_fontbooklet(self):
243 | self.assertRaises(ValueError, FontBooklet, self, self.fp, self.fw)
244 |
245 | def test_iter_fontbooklet(self):
246 | self.fpage.set_header(self.fb)
247 | self.fpage.set_body(self.fb)
248 | self.fpage.set_footer(self.fb)
249 | for page in self.book:
250 | page.draw()
251 |
252 |
253 | if __name__ == '__main__':
254 | unittest.main()
255 |
--------------------------------------------------------------------------------
/docs/source/example.rst:
--------------------------------------------------------------------------------
1 | Example
2 | #######
3 |
4 | Here are some examples that allow basic and advanced use of the library.
5 |
6 | .. toctree::
7 | :maxdepth: 2
8 | :caption: Contents:
9 |
10 | FontPreview example
11 | *******************
12 |
13 | *FontPreview* is a class that creates objects that allow the representation of a font in an image.
14 | By default, it creates a white rectangle with a preview of the letters *a b c d e f* in black.
15 |
16 | .. code-block:: python
17 |
18 | from fontpreview import FontPreview
19 | fp = FontPreview('/tmp/noto.ttf') # path of font file
20 | fp.save('/tmp/fp.png') # default directory is working directory
21 |
22 | This is result:
23 |
24 | .. image:: https://i.ibb.co/258dCPZ/fp.png
25 | :alt: FontPreview image
26 |
27 | Now, let's modify some properties.
28 |
29 | .. code-block:: python
30 |
31 | fp.font_text = 'Welcome to fontpreview'
32 | fp.bg_color = (253, 194, 45) # background color. RGB color: yellow
33 | fp.dimension = (300, 250) # specify dimension in pixel: 300 x 250
34 | fp.fg_color = (51, 153, 193) # foreground or font color. RGB color: blue
35 | fp.set_font_size(50) # set font size to 50 pixel
36 | fp.set_text_position('ltop') # place the text at the top left.
37 | # before saving the image, you need to draw it again
38 | fp.draw()
39 | fp.save('/tmp/fp.png')
40 |
41 |
42 | .. image:: https://i.ibb.co/0rY6YqR/fp.png
43 | :alt: FontPreview image
44 |
45 | A background image can also be set.
46 |
47 | .. code-block:: python
48 |
49 | fp.bg_image = '/tmp/python.png' # a background image
50 | fp.draw() # draw it again
51 | fp.save('/tmp/fp.png')
52 |
53 | .. image:: https://i.ibb.co/RScSMvQ/fp.png
54 | :alt: FontPreview image
55 |
56 | FontBanner example
57 | ******************
58 |
59 | *FontBanner* is a FontPreview-based class, which adds some features to work with one or more objects based on the FontPreview class.
60 | With this object since its creation, it is possible to define the orientation: *landscape* or *portrait*.
61 |
62 | .. code-block:: python
63 |
64 | from fontpreview import FontBanner
65 | fb = FontBanner('/tmp/noto.ttf', 'landscape', bg_color=(253, 194, 45)) # path of font file
66 | fb.save('/tmp/fb.png')
67 |
68 | .. image:: https://i.ibb.co/MPJ1Dr8/fb.png
69 | :alt: FontBanner image
70 |
71 | Let's go and change some of the properties.
72 |
73 | .. code-block:: python
74 |
75 | fb.set_mode('fontname') # set font_text properties to font name
76 | fb.set_orientation('portrait') # set vertical orientation of image
77 | fb.save('/tmp/fb.png')
78 |
79 | .. image:: https://i.ibb.co/RgLSZC1/fb.png
80 | :alt: FontBanner image
81 | :height: 700
82 |
83 | And now, let's add the *FontPreview* object created earlier.
84 |
85 | .. code-block:: python
86 |
87 | fb.font_text = 'Python'
88 | fb.set_font_size(50) # change font size: FontPreview method
89 | fb.bg_color = 'white' # set color with name string
90 | fb.set_orientation((300, 800)) # change orientation and size again with tuple
91 | fb.draw() # draw it again
92 | fb.add_image(fp, (0, 150)) # add FontPreview object to FontBanner object
93 | fb.save('/tmp/fb.png')
94 |
95 | .. image:: https://i.ibb.co/rfMwd7R/fb.png
96 | :alt: FontBanner image
97 |
98 |
99 | FontLogo example
100 | ****************
101 |
102 | *FontLogo* is a FontPreview-based class, which represents a square where inside there are one or two letters.
103 | The fontpreview package logo was generated with this class.
104 |
105 | .. code-block:: python
106 |
107 | from fontpreview import FontLogo
108 | fl = FontLogo('/tmp/noto.ttf', 'Fp') # specify font and letters. Max 2
109 | fl.save('/tmp/fl.png')
110 |
111 | .. image:: https://i.ibb.co/j302Y5k/fl.png
112 | :alt: FontLogo image
113 |
114 | Being a FontPreview based object, it inherits all its characteristics.
115 |
116 | .. code-block:: python
117 |
118 | fl.font_text = 'TS'
119 | fl.bg_color = (45, 121, 199) # background color. RGB color: blue
120 | fl.fg_color = 'white' # foreground color. RGB color: white
121 | fl.set_text_position('rbelow') # position is "right-below"
122 | fl.save('/tmp/fl.png')
123 |
124 | .. image:: https://i.ibb.co/MSFRkfP/fl.png
125 | :alt: FontLogo image
126 |
127 | FontWall example
128 | ****************
129 |
130 | *FontWall* is a class that represents an image in which there are multiple objects based on the FontPreview class.
131 |
132 | This object accepts a list of font paths (with which it automatically builds FontBanner objects) or a list of objects based on the FontPreview class.
133 |
134 | The FontWall object has a mode, which can be *horizontal* or *vertical*, or just specify the usual tuple of with x and y (x, y) axis.
135 | It also accepts a maximum of tiles per row (if the orientation is horizontal) or column (if the orientation is vertical).
136 |
137 | .. code-block:: python
138 |
139 | from fontpreview import FontBanner, FontWall
140 | # Define the various parts of wall
141 | fb = FontBanner('/tmp/noto.ttf', 'landscape' , mode='fontname')
142 | fb2 = FontBanner('/tmp/noto.ttf', 'landscape' , mode='alpha')
143 | fb3 = FontBanner('/tmp/noto.ttf', 'landscape' , mode='letter')
144 | fb4 = FontBanner('/tmp/noto.ttf', 'landscape' , mode='paragraph')
145 | fw = FontWall([fb,fb2,fb3,fb4])
146 | fw.save('/tmp/fw.png')
147 |
148 | .. image:: https://i.ibb.co/cDBST2r/fw.png
149 | :alt: FontWall image
150 |
151 | Any changes made on the parts of the wall are made to the final result.
152 |
153 | .. code-block:: python
154 |
155 | # Modify properties of first banner
156 | fb.font_text = 'Harry Potter'
157 | fb.bg_color = (43, 43, 43)
158 | fb.fg_color = 'white'
159 | fb.set_font_size(120)
160 | # Modify properties of second banner
161 | fb2.font_text = 'Harry Potter is a series of seven fantasy novels\nwritten by British author J. K. Rowling.'
162 | fb2.bg_color = (150, 45, 46)
163 | fb2.fg_color = 'white'
164 | fb2.set_font_size(100)
165 | fb2.set_text_position('ltop')
166 | # Modify properties of third banner
167 | fb3.font_text = 'The series was originally published in English by two major publishers,\nBloomsbury in the United Kingdom and Scholastic Press in the United States. '
168 | fb3.bg_color = (63, 55, 36)
169 | fb3.fg_color = 'white'
170 | fb3.set_font_size(100) # the font is resized automatically because it exceeds the size of the banner
171 | fb3.set_text_position('rcenter')
172 | # Modify properties of last banner
173 | fb4.font_text = 'A series of many genres, including fantasy, drama,\ncoming of age, and the British school story'
174 | fb4.bg_color = (205, 193, 87)
175 | fb4.fg_color = 'black'
176 | fb4.set_font_size(100)
177 | fb4.set_text_position('rbelow')
178 | fw.draw(2) # draw it again, specify max_tile
179 | fw.save('/tmp/fw.png')
180 |
181 | .. image:: https://i.ibb.co/W0B3LYn/fw.png
182 | :alt: FontWall image
183 |
184 | FontPage example
185 | ****************
186 |
187 | *FontPage* is a class that represents a sample page per font. This object consists of three parts: header, body and footer.
188 | These three parts have a standard size defined by a FontPageTemplate (see below).
189 |
190 | .. code-block:: python
191 |
192 | from fontpreview import FontPage, FontBanner
193 | # Define the various parts of wall
194 | header = FontBanner('/tmp/noto.ttf', 'landscape' , mode='fontname')
195 | body = FontBanner('/tmp/noto.ttf', 'landscape' , mode='paragraph')
196 | footer = FontBanner('/tmp/noto.ttf', 'landscape' , mode='letter')
197 | # Create FontPage object
198 | fpage = FontPage()
199 | fpage.set_header(header)
200 | fpage.set_body(body)
201 | fpage.set_footer(footer)
202 | # Design all parts
203 | fpage.draw()
204 | fpage.save('/tmp/fpage.png')
205 |
206 | .. image:: https://i.ibb.co/LgFLnXk/fpage.png
207 | :alt: FontPage image
208 |
209 | Even with this object, any changes made to the individual parts of the page appear in the final result.
210 |
211 | It is also possible to add a FontLogo object to the header, after the header has been defined.
212 |
213 | .. code-block:: python
214 |
215 | from fontpreview import FontLogo
216 | fl = FontLogo('/tmp/noto.ttf', 'Fp') # create logo
217 | fpage.set_logo(fl) # set logo on header
218 | fpage.body.bg_color = (253, 194, 45)
219 | fpage.body.set_font_size(150)
220 | fpage.draw()
221 | fpage.save('/tmp/fpage.png')
222 |
223 | .. image:: https://i.ibb.co/dtt9Ct7/fpage.png
224 | :alt: FontPage image
225 |
226 | FontPageTemplate example
227 | ************************
228 |
229 | *FontPageTemplate* is a class that represents a template applicable to the FontPage object.
230 |
231 | In this object, only the specifications of each part of the FontPage object (header, body, footer) are defined: font size, text position, unit.
232 |
233 | The units (default 6) are equal parts divided across the height of the page.
234 |
235 | .. code-block:: python
236 |
237 | from fontpreview import FontPageTemplate
238 | template = FontPageTemplate(3508) # max height of page
239 | template.set_body(170, 1, 'lcenter') # font_size, units, text_position
240 | template.set_footer(100, 4, 'lcenter') # font_size, units, text_position
241 | # Create FontPage object
242 | fpage = FontPage(template=template)
243 | fpage.set_header(header)
244 | fpage.set_body(body)
245 | fpage.set_footer(footer)
246 | # Design all parts
247 | fpage.draw()
248 | fpage.save('/tmp/fpage_template.png')
249 |
250 | .. image:: https://i.ibb.co/n7L9nNG/fpage-template.png
251 | :alt: FontPage image
252 |
253 | FontBooklet example
254 | *******************
255 |
256 | *FontBooklet* is a class that represents a book of *FontPage* object.
257 |
258 | .. code-block:: python
259 |
260 | from fontpreview import FontPage, FontBanner, FontBooklet
261 | # Define the various parts of page
262 | header = FontBanner('/tmp/noto.ttf', 'landscape' , mode='fontname')
263 | body = FontBanner('/tmp/noto.ttf', 'landscape' , mode='paragraph')
264 | footer = FontBanner('/tmp/noto.ttf', 'landscape' , mode='letter')
265 | # Create FontPage object
266 | fpage1 = FontPage(header=header, body=body, footer=footer)
267 | fpage2 = FontPage(header=header, body=body, footer=footer)
268 | # Design all parts
269 | fpage1.draw()
270 | fpage2.draw()
271 | # Create book
272 | book = FontBooklet(fpage1, fpage2)
273 | book.save('/tmp/noto_book/') # save page1.png, page2.png in /tmp/noto_book/ folder
274 |
275 |
276 | Declarative object creation
277 | ***************************
278 |
279 | Each *FontPreview* and *FontPage* based object in this module has a declarative instance implementation.
280 |
281 | .. code-block:: python
282 |
283 | from fontpreview import FontPreview, FontBanner, FontLogo, FontPage
284 | # FontPreview object
285 | fp = FontPreview('/tmp/noto.ttf',
286 | font_size=50,
287 | font_text='some text',
288 | color_system='RGB',
289 | bg_color='blue',
290 | fg_color='yellow',
291 | dimension=(800, 400))
292 | # FontBanner object
293 | fb = FontBanner('/tmp/noto.ttf',
294 | orientation='portrait',
295 | bg_color='blue',
296 | fg_color='yellow',
297 | mode='paragraph',
298 | font_size=70,
299 | color_system='RGB')
300 | # FontLogo object
301 | fl = FontLogo('/tmp/noto.ttf',
302 | 'Fl',
303 | size=(170, 170),
304 | bg_color='yellow',
305 | fg_color='blue',
306 | font_size=50,
307 | color_system='RGB')
308 | # FontPage object
309 | page = FontPage(header=fb, logo=fl, body=fb, footer=fb)
310 | page.draw()
311 |
--------------------------------------------------------------------------------
/fontpreview/fontbanner.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- encoding: utf-8 -*-
3 | # vim: se ts=4 et syn=python:
4 |
5 | # created by: matteo.guadrini
6 | # fontbanner -- fontpreview
7 | #
8 | # Copyright (C) 2020 Matteo Guadrini
9 | #
10 | # This program is free software: you can redistribute it and/or modify
11 | # it under the terms of the GNU General Public License as published by
12 | # the Free Software Foundation, either version 3 of the License, or
13 | # (at your option) any later version.
14 | #
15 | # This program is distributed in the hope that it will be useful,
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | # GNU General Public License for more details.
19 | #
20 | # You should have received a copy of the GNU General Public License
21 | # along with this program. If not, see .
22 |
23 | # region imports
24 | import os
25 | from .fontpreview import FontPreview
26 | from PIL import Image
27 |
28 |
29 | # endregion
30 |
31 | # region functions
32 | def resize(image, bg_image):
33 | """
34 | Resize image
35 |
36 | :param image: image to resize
37 | :param bg_image: background image
38 | :return: Image object
39 | """
40 | # Check size of background image
41 | new_size = image.size
42 | while new_size > bg_image.size:
43 | width, height = new_size
44 | new_size = (int(width // 1.2), int(height // 1.2))
45 | # Resize image
46 | return image.resize(new_size)
47 |
48 |
49 | # endregion
50 |
51 | # region classes
52 | class FontBanner(FontPreview):
53 | """
54 | Class that represents the banner of a font
55 | """
56 |
57 | def __init__(self, font,
58 | orientation='landscape',
59 | bg_color='white',
60 | fg_color='black',
61 | mode='letter',
62 | font_size=64,
63 | color_system='RGB'
64 | ):
65 | """
66 | Object that represents the banner of a font
67 |
68 | :param font: font file
69 | :param orientation: the orientation of the banner; 'landscape', 'portrait' or tuple(x,y).
70 | :param bg_color: background color of preview. Default is 'white'.
71 | :param fg_color: foreground or font color of preview. Default is 'black'.
72 | :param mode: the text inside the banner; 'letter','fontname', 'paragraph', 'alpha' and 'combination'.
73 | :param font_size: font size. Default is 64.
74 | :param color_system: color system string. Default is 'RGB'.
75 | """
76 | # Define properties
77 | FontPreview.__init__(self, font=font,
78 | bg_color=bg_color,
79 | fg_color=fg_color,
80 | font_size=font_size,
81 | color_system=color_system)
82 | self.set_orientation(orientation)
83 | self.mode = mode
84 | self.set_text_position('center')
85 | # Create default image
86 | self.set_mode(mode=self.mode)
87 |
88 | def __str__(self):
89 | """
90 | String representation of font banner
91 |
92 | :return: string
93 | """
94 | return FontPreview.__str__(self) + ",mode={mode}".format(mode=self.mode)
95 |
96 | def set_orientation(self, orientation, font_position='center'):
97 | """
98 | Set orientation of banner
99 |
100 | :param orientation: the orientation of the banner; 'landscape' or 'portrait'
101 | :param font_position: font position respect dimension of banner
102 | :return: None
103 | """
104 | # Calculate banner size
105 | if isinstance(orientation, tuple):
106 | self.dimension = orientation
107 | # Recalculate font position
108 | self.set_text_position(font_position)
109 | return None
110 | else:
111 | LANDSCAPE = (1653, 560)
112 | PORTRAIT = (560, 1653)
113 | if orientation == 'landscape':
114 | self.dimension = LANDSCAPE
115 | elif orientation == 'portrait':
116 | self.dimension = PORTRAIT
117 | else:
118 | raise ValueError('orientation is "landscape","portrait" or tuple(x,y)')
119 | # Recalculate font position
120 | self.set_text_position(font_position)
121 |
122 | def set_mode(self, mode, align='center'):
123 | """
124 | Set the text mode
125 |
126 | :param mode: mode that sets the text in the banner
127 | :param align: alignment of text. Available 'left', 'center' and 'right'
128 | :return: None
129 | """
130 | MODE = {
131 | 'letter': 'a b c d e f\ng h i j k l\nm n o p q r\ns t u v w x y z',
132 | 'alpha': 'Aa Bb Cc Dd Ee Ff\n1 2 3 4 5 6 7 8 9 0',
133 | 'fontname': '{0}'.format(self.font.getname()[0]),
134 | 'paragraph': 'Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit.',
135 | 'combination': '{0}\n{1}'.format(self.font.getname(),
136 | 'Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit.'
137 | ),
138 | 'none': ''
139 | }
140 | # Verify is mode exists
141 | if mode in MODE:
142 | self.mode = mode
143 | self.font_text = MODE.get(mode)
144 | # Create default image
145 | self.draw(align=align)
146 | else:
147 | raise ValueError('mode is "letter", "alpha", "fontname", "paragraph" and "combination"')
148 |
149 | def add_image(self, image, position):
150 | """
151 | Adds an additional image to the banner
152 |
153 | :param image: path of image
154 | :param position: position of image
155 | :return: None
156 | """
157 | # Create image
158 | if isinstance(image, FontPreview):
159 | img = image.image
160 | else:
161 | img = Image.open(image)
162 | # Check if the image is bigger than the banner
163 | if img.size > self.dimension:
164 | img = resize(img, self.image)
165 | # Add image
166 | self.image.paste(img, position)
167 |
168 |
169 | class FontLogo(FontPreview):
170 | """
171 | Class that represents the logo of a font
172 | """
173 |
174 | def __init__(self, font,
175 | letters,
176 | size=(100, 100),
177 | bg_color='white',
178 | fg_color='black',
179 | font_size=64,
180 | color_system='RGB'
181 | ):
182 | """
183 | Object that represents the logo of a font
184 |
185 | :param font: font file
186 | :param letters: One or two letters (or anything)
187 | :param size: size of logo square. Default is (100, 100)
188 | :param bg_color: background color of preview. Default is 'white'.
189 | :param fg_color: foreground or font color of preview. Default is 'black'.
190 | :param font_size: font size. Default is 64.
191 | :param color_system: color system string. Default is 'RGB'.
192 | """
193 | FontPreview.__init__(self, font=font,
194 | bg_color=bg_color,
195 | fg_color=fg_color,
196 | font_size=font_size,
197 | color_system=color_system)
198 | # Check if the letters exceed the number 2
199 | if len(letters) > 2:
200 | raise ValueError('letters can be maximum two')
201 | else:
202 | self.font_text = letters
203 | # Check maximum size
204 | self.__max_size(size)
205 | # Set letter position
206 | self.set_text_position('center')
207 | # Built a logo font
208 | self.draw()
209 |
210 | def __max_size(self, size):
211 | """
212 | Check maximum size
213 |
214 | :param size: New size
215 | :return: None
216 | """
217 | max_size = ((75, 75), (100, 100), (150, 150), (170, 170))
218 | if size in max_size:
219 | self.dimension = size
220 | else:
221 | raise ValueError('The max size of the logo can be this: (75, 75), (100, 100), (150, 150), (170, 170)')
222 |
223 | def new_size(self, size):
224 | """
225 | Define new size of FontLogo object
226 |
227 | :param size: size of fontlogo object
228 | :return: None
229 | """
230 | # Check maximum size
231 | self.__max_size(size)
232 | # Built a logo font
233 | self.set_text_position('center')
234 |
235 |
236 | class FontWall:
237 | """
238 | Class that represents the wall of fonts
239 | """
240 |
241 | def __init__(self, fonts, max_tile=2, mode='horizontal'):
242 | """
243 | Object that represents the wall of fonts
244 |
245 | :param fonts: font list; string or FontPreview object
246 | :param max_tile: maximum tile per row/column
247 | :param mode: image alignment, 'horizontal' or 'vertical'
248 | """
249 | # Check if list contains string or FontPreview object
250 | if isinstance(fonts, list):
251 | self.fonts = []
252 | for font in fonts:
253 | if isinstance(font, FontPreview):
254 | self.fonts.append(font)
255 | else:
256 | _font = FontBanner(font)
257 | self.fonts.append(_font)
258 | else:
259 | raise TypeError("'fonts' must be a list")
260 | # Other properties
261 | self.color_system = 'RGB'
262 | self.bg_color = 'white'
263 | self.max_width = None
264 | self.max_height = None
265 | self.mode = mode
266 | self.max_tile = max_tile
267 | # Build the wall
268 | self.wall = None
269 | self.draw(self.max_tile)
270 |
271 | def __str__(self):
272 | """
273 | String representation of font wall
274 |
275 | :return: string
276 | """
277 | return str(["tile{0}={1}".format(i, f) for i, f in enumerate(self.fonts)])
278 |
279 | def __concatenate(self, fonts, position):
280 | """
281 | Link multiple images to form a layout inside the wall
282 |
283 | :param fonts: list of FontPreview
284 | :param position: paste positions
285 | :return: tuple
286 | """
287 | # Get max width and height, presume horizontal
288 | if self.mode == 'horizontal':
289 | max_width = sum([font.image.width for font in fonts])
290 | max_height = max([font.image.height for font in fonts])
291 | else:
292 | max_width = max([font.image.width for font in fonts])
293 | max_height = sum([font.image.height for font in fonts])
294 | # Create background
295 | dst = Image.new(self.color_system, (max_width, max_height))
296 | start = 0
297 | for font in fonts:
298 | # Compose the row
299 | if self.mode == 'horizontal':
300 | dst.paste(font.image, (start, 0))
301 | start = font.image.width + start
302 | # Compose the column
303 | elif self.mode == 'vertical':
304 | dst.paste(font.image, (0, start))
305 | start = font.image.height + start
306 | else:
307 | raise ValueError("the mode can be 'horizontal' or 'vertical'")
308 | self.wall.paste(dst, position)
309 | return dst.size
310 |
311 | def draw(self, max_tile):
312 | """
313 | Draw wall with fonts on properties of this object
314 |
315 | :param max_tile: maximum tile per row
316 | :return: None
317 | """
318 | # Split fonts into maximum tile per row/column
319 | fonts = []
320 | for i in range(0, len(self.fonts), max_tile):
321 | fonts.append(self.fonts[i:i + max_tile])
322 | # Calculate max_width and max_height of wall
323 | max_width = []
324 | max_height = []
325 | for font in fonts:
326 | if self.mode == 'horizontal':
327 | max_width.append(sum([f.image.width for f in font]))
328 | max_height.append(max([f.image.height for f in font]))
329 | else:
330 | max_width.append(max([f.image.width for f in font]))
331 | max_height.append(sum([f.image.height for f in font]))
332 | if self.mode == 'horizontal':
333 | self.max_width = max(max_width)
334 | self.max_height = sum(max_height)
335 | else:
336 | self.max_width = sum(max_width)
337 | self.max_height = max(max_height)
338 | self.wall = Image.new(self.color_system, (self.max_width, self.max_height), color=self.bg_color)
339 | # Build the wall
340 | start_position = (0, 0)
341 | for font in fonts:
342 | if self.mode == 'horizontal':
343 | last_position = self.__concatenate(font, start_position)
344 | start_position = (0, (start_position[1] + last_position[1]))
345 | else:
346 | last_position = self.__concatenate(font, start_position)
347 | start_position = ((start_position[0] + last_position[0]), 0)
348 |
349 | def save(self, path=os.path.join(os.path.abspath(os.getcwd()), 'fontwall.png')):
350 | """
351 | Save the font wall
352 |
353 | :param path: path where you want to save the font wall
354 | :return: None
355 | """
356 | self.wall.save(path)
357 |
358 | def show(self):
359 | """
360 | Displays this image.
361 |
362 | :return: None
363 | """
364 | self.wall.show()
365 |
366 | # endregion
367 |
--------------------------------------------------------------------------------
/fontpreview/fontpage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- encoding: utf-8 -*-
3 | # vim: se ts=4 et syn=python:
4 |
5 | # created by: matteo.guadrini
6 | # fontpage -- fontpreview
7 | #
8 | # Copyright (C) 2020 Matteo Guadrini
9 | #
10 | # This program is free software: you can redistribute it and/or modify
11 | # it under the terms of the GNU General Public License as published by
12 | # the Free Software Foundation, either version 3 of the License, or
13 | # (at your option) any later version.
14 | #
15 | # This program is distributed in the hope that it will be useful,
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | # GNU General Public License for more details.
19 | #
20 | # You should have received a copy of the GNU General Public License
21 | # along with this program. If not, see .
22 |
23 | # region imports
24 | import os
25 | from .fontpreview import FontPreview, CALC_POSITION
26 | from .fontbanner import FontLogo
27 | from PIL import Image, ImageDraw
28 |
29 |
30 | # endregion
31 |
32 | # region classes
33 | class FontPage:
34 | """
35 | Class that represents the page of a font banners
36 | """
37 |
38 | def __init__(self, template=None, dimension=(2480, 3508), header=None, logo=None, body=None, footer=None):
39 | """
40 | Object that represents the page of a font banners
41 |
42 | :param template: template used to build the page
43 | :param dimension: dimension of page. Default A4 in pixels.
44 | :param header: header of fontpage object
45 | :param logo: logo of fontpage object on header part
46 | :param body: body of fontpage object
47 | :param footer: footer of fontpage object
48 | """
49 | self.template = template
50 | if self.template:
51 | self.dimension = (dimension[0], self.template.page_height)
52 | else:
53 | self.dimension = dimension
54 | self.color_system = 'RGB'
55 | self.page = Image.new(self.color_system, self.dimension, color='white')
56 | # Set header
57 | if header:
58 | self.set_header(header)
59 | else:
60 | self.header = None
61 | # Set logo
62 | if logo:
63 | self.set_logo(logo)
64 | else:
65 | self.logo = None
66 | # Set body
67 | if body:
68 | self.set_body(body)
69 | else:
70 | self.body = None
71 | # Set footer
72 | if footer:
73 | self.set_footer(footer)
74 | else:
75 | self.footer = None
76 |
77 | def __str__(self):
78 | """
79 | String representation of font page
80 |
81 | :return: string
82 | """
83 | return 'header={0}, body={1}, footer={2}'.format(self.header, self.body, self.footer)
84 |
85 | def __compose(self):
86 | """
87 | Dynamically compose the page
88 |
89 | :return: None
90 | """
91 | # Check if the template is specified
92 | if not self.template:
93 | self.template = FontPageTemplate(self.dimension[1])
94 | # Check height of each part
95 | try:
96 | # Compose background
97 | self.dimension = (self.dimension[0], self.template.page_height)
98 | self.page = Image.new(self.color_system, self.dimension, color='white')
99 | # Compose header
100 | self.set_header(self.header)
101 | if self.header.image.height != self.template.header_units:
102 | self.header.dimension = (self.page.width, self.template.header_units)
103 | self.header.set_font_size(self.template.header_font_size)
104 | self.header.set_text_position(self.template.header_text_position)
105 | # Compose body
106 | self.set_body(self.body)
107 | if self.body.image.height != self.template.body_units:
108 | self.body.dimension = (self.page.width, self.template.body_units)
109 | self.body.set_font_size(self.template.body_font_size)
110 | self.body.set_text_position(self.template.body_text_position)
111 | # Compose footer
112 | self.set_footer(self.footer)
113 | if self.footer.image.height != self.template.footer_units:
114 | self.footer.dimension = (self.page.width, self.template.footer_units)
115 | self.footer.set_font_size(self.template.footer_font_size)
116 | self.footer.set_text_position(self.template.footer_text_position)
117 | except AttributeError:
118 | raise AttributeError('header, body and footer is mandatory object')
119 |
120 | def set_header(self, header):
121 | """
122 | Set header of Font page
123 |
124 | :param header: FontPreview object
125 | :return: None
126 | """
127 | # Check if header is FontPreview object
128 | if isinstance(header, FontPreview):
129 | # Check width of header
130 | if self.page.width != header.image.width:
131 | header.dimension = (self.page.width, header.image.height)
132 | header.draw()
133 | self.header = header
134 | else:
135 | raise ValueError('header must be FontPreview based object, not {0}'.format(header))
136 |
137 | def set_logo(self, logo):
138 | """
139 | Set logo of Font page
140 |
141 | :param logo: FontLogo object
142 | :return: None
143 | """
144 | # Check if logo is FontLogo object
145 | if isinstance(logo, FontLogo):
146 | # Check if header exists
147 | if self.header:
148 | # Check size of header
149 | if self.header.image.size < logo.image.size:
150 | logo.new_size((75, 75))
151 | # Add logo on header
152 | self.header.add_image(logo, CALC_POSITION['lcenter'](self.header.dimension,
153 | self.header.font.getsize(self.header.font_text)))
154 | else:
155 | raise AttributeError('header attribute is None')
156 | else:
157 | raise ValueError('logo must be FontLogo object, not {0}'.format(logo))
158 |
159 | def set_body(self, body):
160 | """
161 | Set body of Font page
162 |
163 | :param body: FontPreview object
164 | :return: None
165 | """
166 | # Check if body is FontPreview object
167 | if isinstance(body, FontPreview):
168 | # Check width of body
169 | if self.page.width != body.image.width:
170 | body.dimension = (self.page.width, body.image.height)
171 | body.draw()
172 | self.body = body
173 | else:
174 | raise ValueError('body must be FontPreview based object, not {0}'.format(body))
175 |
176 | def set_footer(self, footer):
177 | """
178 | Set footer of Font page
179 |
180 | :param footer: FontPreview object
181 | :return: None
182 | """
183 | # Check if footer is FontPreview object
184 | if isinstance(footer, FontPreview):
185 | # Check width of footer
186 | if self.page.width != footer.image.width:
187 | footer.dimension = (self.page.width, footer.image.height)
188 | footer.draw()
189 | self.footer = footer
190 | else:
191 | raise ValueError('footer must be FontPreview based object, not {0}'.format(footer))
192 |
193 | def draw(self, separator=True, sep_color='black', sep_width=5):
194 | """
195 | Draw font page with header, logo, body and footer
196 |
197 | :param separator: line that separates the parts
198 | :param sep_color: separator color
199 | :param sep_width: separator width
200 | :return: None
201 | """
202 | # Compose all parts
203 | self.__compose()
204 | header_start = (0, 0)
205 | self.page.paste(self.header.image, header_start)
206 | body_start = (0, self.header.image.height)
207 | self.page.paste(self.body.image, body_start)
208 | footer_start = (0, (self.body.image.height + self.header.image.height))
209 | self.page.paste(self.footer.image, footer_start)
210 | # Draw line
211 | if separator:
212 | draw = ImageDraw.Draw(self.page)
213 | # Header/Body line
214 | body_finish = (self.page.width, self.header.image.height)
215 | draw.line([body_start, body_finish], fill=sep_color, width=sep_width)
216 | # Body/Footer line
217 | footer_finish = (self.page.width, self.body.image.height + self.header.image.height)
218 | draw.line([footer_start, footer_finish], fill=sep_color, width=sep_width)
219 |
220 | def save(self, path=os.path.join(os.path.abspath(os.getcwd()), 'fontpage.png')):
221 | """
222 | Save the font page
223 |
224 | :param path: path where you want to save the font page
225 | :return: None
226 | """
227 | self.page.save(path)
228 |
229 | def show(self):
230 | """
231 | Displays this image.
232 |
233 | :return: None
234 | """
235 | self.page.show()
236 |
237 |
238 | class FontPageTemplate:
239 | """
240 | Class representing the template of a FontPage object
241 | """
242 |
243 | def __init__(self, page_height=3508, units_number=6):
244 | """
245 | Object representing the template of a FontPage object
246 |
247 | :param page_height: height of FontPage object. Default is 3508.
248 | :param units_number: division number to create the units
249 | """
250 | # Calculate units
251 | self.page_height = page_height
252 | self.unit = self.page_height // units_number
253 | # header
254 | self.header_font_size = 120
255 | self.header_units = self.unit
256 | self.header_text_position = 'center'
257 | # body
258 | self.body_font_size = 140
259 | self.body_units = self.unit * 3
260 | self.body_text_position = 'center'
261 | # footer
262 | self.footer_font_size = 120
263 | self.footer_units = self.unit * 2
264 | self.footer_text_position = 'center'
265 |
266 | def __str__(self):
267 | """
268 | String representation of font page
269 |
270 | :return: string
271 | """
272 | return 'page_height={0}, unit={1}'.format(self.page_height, self.unit)
273 |
274 | def __check_units(self, context, unit):
275 | """
276 | Check the overrun of the units
277 |
278 | :param context: context is part of page; header, body or footer
279 | :param unit: height of unit
280 | :return: None
281 | """
282 | # Check context
283 | if context == 'header':
284 | total_units = unit + self.body_units + self.footer_units
285 | elif context == 'body':
286 | total_units = self.header_units + unit + self.footer_units
287 | elif context == 'footer':
288 | total_units = self.header_units + self.body_units + unit
289 | else:
290 | raise ValueError('context must be "header","body" or "footer"')
291 | # Check if total units overrun maximum height
292 | if total_units > self.page_height:
293 | raise ValueError('The height of the units exceed the maximum allowed: {0}'.format(self.page_height))
294 |
295 | def set_header(self, font_size, units, text_position):
296 | """
297 | Setting the header properties
298 |
299 | :param font_size: the header font size
300 | :param units: the header units number
301 | :param text_position: the header text position
302 | :return: None
303 | """
304 | # header
305 | self.header_font_size = font_size
306 | unit = self.unit * units
307 | self.__check_units('header', unit)
308 | self.header_units = unit
309 | self.header_text_position = text_position
310 |
311 | def set_body(self, font_size, units, text_position):
312 | """
313 | Setting the body properties
314 |
315 | :param font_size: the body font size
316 | :param units: the body units number
317 | :param text_position: the body text position
318 | :return: None
319 | """
320 | # header
321 | self.body_font_size = font_size
322 | unit = self.unit * units
323 | self.__check_units('body', unit)
324 | self.body_units = unit
325 | self.body_text_position = text_position
326 |
327 | def set_footer(self, font_size, units, text_position):
328 | """
329 | Setting the footer properties
330 |
331 | :param font_size: the footer font size
332 | :param units: the footer units number
333 | :param text_position: the footer text position
334 | :return: None
335 | """
336 | # header
337 | self.footer_font_size = font_size
338 | unit = self.unit * units
339 | self.__check_units('footer', unit)
340 | self.footer_units = unit
341 | self.footer_text_position = text_position
342 |
343 |
344 | class FontBooklet:
345 | """
346 | Class that represents the booklet of a font page
347 | """
348 |
349 | def __init__(self, *pages):
350 | """
351 | Object that represents the booklet of a font page
352 |
353 | :param pages: FontPage's object
354 | """
355 | self.pages = []
356 | # Check foreach pages if FontPage object
357 | for page in pages:
358 | if isinstance(page, FontPage):
359 | self.pages.append(page)
360 | else:
361 | raise ValueError("{0} isn't FontPage object".format(page))
362 |
363 | def __iter__(self):
364 | """
365 | Iterating on each FontPage
366 |
367 | :return: next value
368 | """
369 | return iter(self.pages)
370 |
371 | def save(self, folder, extension='png'):
372 | """
373 | Save on each FontPage image
374 |
375 | :param folder: path folder where you want to save each font page
376 | :param extension: extension of imge file. Default is 'png'
377 | :return: None
378 | """
379 | # Define counter page
380 | page_counter = 1
381 | # Check folder path exists
382 | if not os.path.exists(folder):
383 | os.makedirs(folder)
384 | # Save all page in folder path
385 | for page in self:
386 | page.save(os.path.join(folder, 'page{0}.{1}'.format(page_counter, extension)))
387 | page_counter += 1
388 |
389 | # endregion
390 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | ### GNU GENERAL PUBLIC LICENSE
2 |
3 | Version 3, 29 June 2007
4 |
5 | Copyright (C) 2007 Free Software Foundation, Inc.
6 |
7 |
8 | Everyone is permitted to copy and distribute verbatim copies of this
9 | license document, but changing it is not allowed.
10 |
11 | ### Preamble
12 |
13 | The GNU General Public License is a free, copyleft license for
14 | software and other kinds of works.
15 |
16 | The licenses for most software and other practical works are designed
17 | to take away your freedom to share and change the works. By contrast,
18 | the GNU General Public License is intended to guarantee your freedom
19 | to share and change all versions of a program--to make sure it remains
20 | free software for all its users. We, the Free Software Foundation, use
21 | the GNU General Public License for most of our software; it applies
22 | also to any other work released this way by its authors. You can apply
23 | it to your programs, too.
24 |
25 | When we speak of free software, we are referring to freedom, not
26 | price. Our General Public Licenses are designed to make sure that you
27 | have the freedom to distribute copies of free software (and charge for
28 | them if you wish), that you receive source code or can get it if you
29 | want it, that you can change the software or use pieces of it in new
30 | free programs, and that you know you can do these things.
31 |
32 | To protect your rights, we need to prevent others from denying you
33 | these rights or asking you to surrender the rights. Therefore, you
34 | have certain responsibilities if you distribute copies of the
35 | software, or if you modify it: responsibilities to respect the freedom
36 | of others.
37 |
38 | For example, if you distribute copies of such a program, whether
39 | gratis or for a fee, you must pass on to the recipients the same
40 | freedoms that you received. You must make sure that they, too, receive
41 | or can get the source code. And you must show them these terms so they
42 | know their rights.
43 |
44 | Developers that use the GNU GPL protect your rights with two steps:
45 | (1) assert copyright on the software, and (2) offer you this License
46 | giving you legal permission to copy, distribute and/or modify it.
47 |
48 | For the developers' and authors' protection, the GPL clearly explains
49 | that there is no warranty for this free software. For both users' and
50 | authors' sake, the GPL requires that modified versions be marked as
51 | changed, so that their problems will not be attributed erroneously to
52 | authors of previous versions.
53 |
54 | Some devices are designed to deny users access to install or run
55 | modified versions of the software inside them, although the
56 | manufacturer can do so. This is fundamentally incompatible with the
57 | aim of protecting users' freedom to change the software. The
58 | systematic pattern of such abuse occurs in the area of products for
59 | individuals to use, which is precisely where it is most unacceptable.
60 | Therefore, we have designed this version of the GPL to prohibit the
61 | practice for those products. If such problems arise substantially in
62 | other domains, we stand ready to extend this provision to those
63 | domains in future versions of the GPL, as needed to protect the
64 | freedom of users.
65 |
66 | Finally, every program is threatened constantly by software patents.
67 | States should not allow patents to restrict development and use of
68 | software on general-purpose computers, but in those that do, we wish
69 | to avoid the special danger that patents applied to a free program
70 | could make it effectively proprietary. To prevent this, the GPL
71 | assures that patents cannot be used to render the program non-free.
72 |
73 | The precise terms and conditions for copying, distribution and
74 | modification follow.
75 |
76 | ### TERMS AND CONDITIONS
77 |
78 | #### 0. Definitions.
79 |
80 | "This License" refers to version 3 of the GNU General Public License.
81 |
82 | "Copyright" also means copyright-like laws that apply to other kinds
83 | of works, such as semiconductor masks.
84 |
85 | "The Program" refers to any copyrightable work licensed under this
86 | License. Each licensee is addressed as "you". "Licensees" and
87 | "recipients" may be individuals or organizations.
88 |
89 | To "modify" a work means to copy from or adapt all or part of the work
90 | in a fashion requiring copyright permission, other than the making of
91 | an exact copy. The resulting work is called a "modified version" of
92 | the earlier work or a work "based on" the earlier work.
93 |
94 | A "covered work" means either the unmodified Program or a work based
95 | on the Program.
96 |
97 | To "propagate" a work means to do anything with it that, without
98 | permission, would make you directly or secondarily liable for
99 | infringement under applicable copyright law, except executing it on a
100 | computer or modifying a private copy. Propagation includes copying,
101 | distribution (with or without modification), making available to the
102 | public, and in some countries other activities as well.
103 |
104 | To "convey" a work means any kind of propagation that enables other
105 | parties to make or receive copies. Mere interaction with a user
106 | through a computer network, with no transfer of a copy, is not
107 | conveying.
108 |
109 | An interactive user interface displays "Appropriate Legal Notices" to
110 | the extent that it includes a convenient and prominently visible
111 | feature that (1) displays an appropriate copyright notice, and (2)
112 | tells the user that there is no warranty for the work (except to the
113 | extent that warranties are provided), that licensees may convey the
114 | work under this License, and how to view a copy of this License. If
115 | the interface presents a list of user commands or options, such as a
116 | menu, a prominent item in the list meets this criterion.
117 |
118 | #### 1. Source Code.
119 |
120 | The "source code" for a work means the preferred form of the work for
121 | making modifications to it. "Object code" means any non-source form of
122 | a work.
123 |
124 | A "Standard Interface" means an interface that either is an official
125 | standard defined by a recognized standards body, or, in the case of
126 | interfaces specified for a particular programming language, one that
127 | is widely used among developers working in that language.
128 |
129 | The "System Libraries" of an executable work include anything, other
130 | than the work as a whole, that (a) is included in the normal form of
131 | packaging a Major Component, but which is not part of that Major
132 | Component, and (b) serves only to enable use of the work with that
133 | Major Component, or to implement a Standard Interface for which an
134 | implementation is available to the public in source code form. A
135 | "Major Component", in this context, means a major essential component
136 | (kernel, window system, and so on) of the specific operating system
137 | (if any) on which the executable work runs, or a compiler used to
138 | produce the work, or an object code interpreter used to run it.
139 |
140 | The "Corresponding Source" for a work in object code form means all
141 | the source code needed to generate, install, and (for an executable
142 | work) run the object code and to modify the work, including scripts to
143 | control those activities. However, it does not include the work's
144 | System Libraries, or general-purpose tools or generally available free
145 | programs which are used unmodified in performing those activities but
146 | which are not part of the work. For example, Corresponding Source
147 | includes interface definition files associated with source files for
148 | the work, and the source code for shared libraries and dynamically
149 | linked subprograms that the work is specifically designed to require,
150 | such as by intimate data communication or control flow between those
151 | subprograms and other parts of the work.
152 |
153 | The Corresponding Source need not include anything that users can
154 | regenerate automatically from other parts of the Corresponding Source.
155 |
156 | The Corresponding Source for a work in source code form is that same
157 | work.
158 |
159 | #### 2. Basic Permissions.
160 |
161 | All rights granted under this License are granted for the term of
162 | copyright on the Program, and are irrevocable provided the stated
163 | conditions are met. This License explicitly affirms your unlimited
164 | permission to run the unmodified Program. The output from running a
165 | covered work is covered by this License only if the output, given its
166 | content, constitutes a covered work. This License acknowledges your
167 | rights of fair use or other equivalent, as provided by copyright law.
168 |
169 | You may make, run and propagate covered works that you do not convey,
170 | without conditions so long as your license otherwise remains in force.
171 | You may convey covered works to others for the sole purpose of having
172 | them make modifications exclusively for you, or provide you with
173 | facilities for running those works, provided that you comply with the
174 | terms of this License in conveying all material for which you do not
175 | control copyright. Those thus making or running the covered works for
176 | you must do so exclusively on your behalf, under your direction and
177 | control, on terms that prohibit them from making any copies of your
178 | copyrighted material outside their relationship with you.
179 |
180 | Conveying under any other circumstances is permitted solely under the
181 | conditions stated below. Sublicensing is not allowed; section 10 makes
182 | it unnecessary.
183 |
184 | #### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
185 |
186 | No covered work shall be deemed part of an effective technological
187 | measure under any applicable law fulfilling obligations under article
188 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
189 | similar laws prohibiting or restricting circumvention of such
190 | measures.
191 |
192 | When you convey a covered work, you waive any legal power to forbid
193 | circumvention of technological measures to the extent such
194 | circumvention is effected by exercising rights under this License with
195 | respect to the covered work, and you disclaim any intention to limit
196 | operation or modification of the work as a means of enforcing, against
197 | the work's users, your or third parties' legal rights to forbid
198 | circumvention of technological measures.
199 |
200 | #### 4. Conveying Verbatim Copies.
201 |
202 | You may convey verbatim copies of the Program's source code as you
203 | receive it, in any medium, provided that you conspicuously and
204 | appropriately publish on each copy an appropriate copyright notice;
205 | keep intact all notices stating that this License and any
206 | non-permissive terms added in accord with section 7 apply to the code;
207 | keep intact all notices of the absence of any warranty; and give all
208 | recipients a copy of this License along with the Program.
209 |
210 | You may charge any price or no price for each copy that you convey,
211 | and you may offer support or warranty protection for a fee.
212 |
213 | #### 5. Conveying Modified Source Versions.
214 |
215 | You may convey a work based on the Program, or the modifications to
216 | produce it from the Program, in the form of source code under the
217 | terms of section 4, provided that you also meet all of these
218 | conditions:
219 |
220 | - a) The work must carry prominent notices stating that you modified
221 | it, and giving a relevant date.
222 | - b) The work must carry prominent notices stating that it is
223 | released under this License and any conditions added under
224 | section 7. This requirement modifies the requirement in section 4
225 | to "keep intact all notices".
226 | - c) You must license the entire work, as a whole, under this
227 | License to anyone who comes into possession of a copy. This
228 | License will therefore apply, along with any applicable section 7
229 | additional terms, to the whole of the work, and all its parts,
230 | regardless of how they are packaged. This License gives no
231 | permission to license the work in any other way, but it does not
232 | invalidate such permission if you have separately received it.
233 | - d) If the work has interactive user interfaces, each must display
234 | Appropriate Legal Notices; however, if the Program has interactive
235 | interfaces that do not display Appropriate Legal Notices, your
236 | work need not make them do so.
237 |
238 | A compilation of a covered work with other separate and independent
239 | works, which are not by their nature extensions of the covered work,
240 | and which are not combined with it such as to form a larger program,
241 | in or on a volume of a storage or distribution medium, is called an
242 | "aggregate" if the compilation and its resulting copyright are not
243 | used to limit the access or legal rights of the compilation's users
244 | beyond what the individual works permit. Inclusion of a covered work
245 | in an aggregate does not cause this License to apply to the other
246 | parts of the aggregate.
247 |
248 | #### 6. Conveying Non-Source Forms.
249 |
250 | You may convey a covered work in object code form under the terms of
251 | sections 4 and 5, provided that you also convey the machine-readable
252 | Corresponding Source under the terms of this License, in one of these
253 | ways:
254 |
255 | - a) Convey the object code in, or embodied in, a physical product
256 | (including a physical distribution medium), accompanied by the
257 | Corresponding Source fixed on a durable physical medium
258 | customarily used for software interchange.
259 | - b) Convey the object code in, or embodied in, a physical product
260 | (including a physical distribution medium), accompanied by a
261 | written offer, valid for at least three years and valid for as
262 | long as you offer spare parts or customer support for that product
263 | model, to give anyone who possesses the object code either (1) a
264 | copy of the Corresponding Source for all the software in the
265 | product that is covered by this License, on a durable physical
266 | medium customarily used for software interchange, for a price no
267 | more than your reasonable cost of physically performing this
268 | conveying of source, or (2) access to copy the Corresponding
269 | Source from a network server at no charge.
270 | - c) Convey individual copies of the object code with a copy of the
271 | written offer to provide the Corresponding Source. This
272 | alternative is allowed only occasionally and noncommercially, and
273 | only if you received the object code with such an offer, in accord
274 | with subsection 6b.
275 | - d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 | - e) Convey the object code using peer-to-peer transmission,
288 | provided you inform other peers where the object code and
289 | Corresponding Source of the work are being offered to the general
290 | public at no charge under subsection 6d.
291 |
292 | A separable portion of the object code, whose source code is excluded
293 | from the Corresponding Source as a System Library, need not be
294 | included in conveying the object code work.
295 |
296 | A "User Product" is either (1) a "consumer product", which means any
297 | tangible personal property which is normally used for personal,
298 | family, or household purposes, or (2) anything designed or sold for
299 | incorporation into a dwelling. In determining whether a product is a
300 | consumer product, doubtful cases shall be resolved in favor of
301 | coverage. For a particular product received by a particular user,
302 | "normally used" refers to a typical or common use of that class of
303 | product, regardless of the status of the particular user or of the way
304 | in which the particular user actually uses, or expects or is expected
305 | to use, the product. A product is a consumer product regardless of
306 | whether the product has substantial commercial, industrial or
307 | non-consumer uses, unless such uses represent the only significant
308 | mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to
312 | install and execute modified versions of a covered work in that User
313 | Product from a modified version of its Corresponding Source. The
314 | information must suffice to ensure that the continued functioning of
315 | the modified object code is in no case prevented or interfered with
316 | solely because modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or
331 | updates for a work that has been modified or installed by the
332 | recipient, or for the User Product in which it has been modified or
333 | installed. Access to a network may be denied when the modification
334 | itself materially and adversely affects the operation of the network
335 | or violates the rules and protocols for communication across the
336 | network.
337 |
338 | Corresponding Source conveyed, and Installation Information provided,
339 | in accord with this section must be in a format that is publicly
340 | documented (and with an implementation available to the public in
341 | source code form), and must require no special password or key for
342 | unpacking, reading or copying.
343 |
344 | #### 7. Additional Terms.
345 |
346 | "Additional permissions" are terms that supplement the terms of this
347 | License by making exceptions from one or more of its conditions.
348 | Additional permissions that are applicable to the entire Program shall
349 | be treated as though they were included in this License, to the extent
350 | that they are valid under applicable law. If additional permissions
351 | apply only to part of the Program, that part may be used separately
352 | under those permissions, but the entire Program remains governed by
353 | this License without regard to the additional permissions.
354 |
355 | When you convey a copy of a covered work, you may at your option
356 | remove any additional permissions from that copy, or from any part of
357 | it. (Additional permissions may be written to require their own
358 | removal in certain cases when you modify the work.) You may place
359 | additional permissions on material, added by you to a covered work,
360 | for which you have or can give appropriate copyright permission.
361 |
362 | Notwithstanding any other provision of this License, for material you
363 | add to a covered work, you may (if authorized by the copyright holders
364 | of that material) supplement the terms of this License with terms:
365 |
366 | - a) Disclaiming warranty or limiting liability differently from the
367 | terms of sections 15 and 16 of this License; or
368 | - b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 | - c) Prohibiting misrepresentation of the origin of that material,
372 | or requiring that modified versions of such material be marked in
373 | reasonable ways as different from the original version; or
374 | - d) Limiting the use for publicity purposes of names of licensors
375 | or authors of the material; or
376 | - e) Declining to grant rights under trademark law for use of some
377 | trade names, trademarks, or service marks; or
378 | - f) Requiring indemnification of licensors and authors of that
379 | material by anyone who conveys the material (or modified versions
380 | of it) with contractual assumptions of liability to the recipient,
381 | for any liability that these contractual assumptions directly
382 | impose on those licensors and authors.
383 |
384 | All other non-permissive additional terms are considered "further
385 | restrictions" within the meaning of section 10. If the Program as you
386 | received it, or any part of it, contains a notice stating that it is
387 | governed by this License along with a term that is a further
388 | restriction, you may remove that term. If a license document contains
389 | a further restriction but permits relicensing or conveying under this
390 | License, you may add to a covered work material governed by the terms
391 | of that license document, provided that the further restriction does
392 | not survive such relicensing or conveying.
393 |
394 | If you add terms to a covered work in accord with this section, you
395 | must place, in the relevant source files, a statement of the
396 | additional terms that apply to those files, or a notice indicating
397 | where to find the applicable terms.
398 |
399 | Additional terms, permissive or non-permissive, may be stated in the
400 | form of a separately written license, or stated as exceptions; the
401 | above requirements apply either way.
402 |
403 | #### 8. Termination.
404 |
405 | You may not propagate or modify a covered work except as expressly
406 | provided under this License. Any attempt otherwise to propagate or
407 | modify it is void, and will automatically terminate your rights under
408 | this License (including any patent licenses granted under the third
409 | paragraph of section 11).
410 |
411 | However, if you cease all violation of this License, then your license
412 | from a particular copyright holder is reinstated (a) provisionally,
413 | unless and until the copyright holder explicitly and finally
414 | terminates your license, and (b) permanently, if the copyright holder
415 | fails to notify you of the violation by some reasonable means prior to
416 | 60 days after the cessation.
417 |
418 | Moreover, your license from a particular copyright holder is
419 | reinstated permanently if the copyright holder notifies you of the
420 | violation by some reasonable means, this is the first time you have
421 | received notice of violation of this License (for any work) from that
422 | copyright holder, and you cure the violation prior to 30 days after
423 | your receipt of the notice.
424 |
425 | Termination of your rights under this section does not terminate the
426 | licenses of parties who have received copies or rights from you under
427 | this License. If your rights have been terminated and not permanently
428 | reinstated, you do not qualify to receive new licenses for the same
429 | material under section 10.
430 |
431 | #### 9. Acceptance Not Required for Having Copies.
432 |
433 | You are not required to accept this License in order to receive or run
434 | a copy of the Program. Ancillary propagation of a covered work
435 | occurring solely as a consequence of using peer-to-peer transmission
436 | to receive a copy likewise does not require acceptance. However,
437 | nothing other than this License grants you permission to propagate or
438 | modify any covered work. These actions infringe copyright if you do
439 | not accept this License. Therefore, by modifying or propagating a
440 | covered work, you indicate your acceptance of this License to do so.
441 |
442 | #### 10. Automatic Licensing of Downstream Recipients.
443 |
444 | Each time you convey a covered work, the recipient automatically
445 | receives a license from the original licensors, to run, modify and
446 | propagate that work, subject to this License. You are not responsible
447 | for enforcing compliance by third parties with this License.
448 |
449 | An "entity transaction" is a transaction transferring control of an
450 | organization, or substantially all assets of one, or subdividing an
451 | organization, or merging organizations. If propagation of a covered
452 | work results from an entity transaction, each party to that
453 | transaction who receives a copy of the work also receives whatever
454 | licenses to the work the party's predecessor in interest had or could
455 | give under the previous paragraph, plus a right to possession of the
456 | Corresponding Source of the work from the predecessor in interest, if
457 | the predecessor has it or can get it with reasonable efforts.
458 |
459 | You may not impose any further restrictions on the exercise of the
460 | rights granted or affirmed under this License. For example, you may
461 | not impose a license fee, royalty, or other charge for exercise of
462 | rights granted under this License, and you may not initiate litigation
463 | (including a cross-claim or counterclaim in a lawsuit) alleging that
464 | any patent claim is infringed by making, using, selling, offering for
465 | sale, or importing the Program or any portion of it.
466 |
467 | #### 11. Patents.
468 |
469 | A "contributor" is a copyright holder who authorizes use under this
470 | License of the Program or a work on which the Program is based. The
471 | work thus licensed is called the contributor's "contributor version".
472 |
473 | A contributor's "essential patent claims" are all patent claims owned
474 | or controlled by the contributor, whether already acquired or
475 | hereafter acquired, that would be infringed by some manner, permitted
476 | by this License, of making, using, or selling its contributor version,
477 | but do not include claims that would be infringed only as a
478 | consequence of further modification of the contributor version. For
479 | purposes of this definition, "control" includes the right to grant
480 | patent sublicenses in a manner consistent with the requirements of
481 | this License.
482 |
483 | Each contributor grants you a non-exclusive, worldwide, royalty-free
484 | patent license under the contributor's essential patent claims, to
485 | make, use, sell, offer for sale, import and otherwise run, modify and
486 | propagate the contents of its contributor version.
487 |
488 | In the following three paragraphs, a "patent license" is any express
489 | agreement or commitment, however denominated, not to enforce a patent
490 | (such as an express permission to practice a patent or covenant not to
491 | sue for patent infringement). To "grant" such a patent license to a
492 | party means to make such an agreement or commitment not to enforce a
493 | patent against the party.
494 |
495 | If you convey a covered work, knowingly relying on a patent license,
496 | and the Corresponding Source of the work is not available for anyone
497 | to copy, free of charge and under the terms of this License, through a
498 | publicly available network server or other readily accessible means,
499 | then you must either (1) cause the Corresponding Source to be so
500 | available, or (2) arrange to deprive yourself of the benefit of the
501 | patent license for this particular work, or (3) arrange, in a manner
502 | consistent with the requirements of this License, to extend the patent
503 | license to downstream recipients. "Knowingly relying" means you have
504 | actual knowledge that, but for the patent license, your conveying the
505 | covered work in a country, or your recipient's use of the covered work
506 | in a country, would infringe one or more identifiable patents in that
507 | country that you have reason to believe are valid.
508 |
509 | If, pursuant to or in connection with a single transaction or
510 | arrangement, you convey, or propagate by procuring conveyance of, a
511 | covered work, and grant a patent license to some of the parties
512 | receiving the covered work authorizing them to use, propagate, modify
513 | or convey a specific copy of the covered work, then the patent license
514 | you grant is automatically extended to all recipients of the covered
515 | work and works based on it.
516 |
517 | A patent license is "discriminatory" if it does not include within the
518 | scope of its coverage, prohibits the exercise of, or is conditioned on
519 | the non-exercise of one or more of the rights that are specifically
520 | granted under this License. You may not convey a covered work if you
521 | are a party to an arrangement with a third party that is in the
522 | business of distributing software, under which you make payment to the
523 | third party based on the extent of your activity of conveying the
524 | work, and under which the third party grants, to any of the parties
525 | who would receive the covered work from you, a discriminatory patent
526 | license (a) in connection with copies of the covered work conveyed by
527 | you (or copies made from those copies), or (b) primarily for and in
528 | connection with specific products or compilations that contain the
529 | covered work, unless you entered into that arrangement, or that patent
530 | license was granted, prior to 28 March 2007.
531 |
532 | Nothing in this License shall be construed as excluding or limiting
533 | any implied license or other defenses to infringement that may
534 | otherwise be available to you under applicable patent law.
535 |
536 | #### 12. No Surrender of Others' Freedom.
537 |
538 | If conditions are imposed on you (whether by court order, agreement or
539 | otherwise) that contradict the conditions of this License, they do not
540 | excuse you from the conditions of this License. If you cannot convey a
541 | covered work so as to satisfy simultaneously your obligations under
542 | this License and any other pertinent obligations, then as a
543 | consequence you may not convey it at all. For example, if you agree to
544 | terms that obligate you to collect a royalty for further conveying
545 | from those to whom you convey the Program, the only way you could
546 | satisfy both those terms and this License would be to refrain entirely
547 | from conveying the Program.
548 |
549 | #### 13. Use with the GNU Affero General Public License.
550 |
551 | Notwithstanding any other provision of this License, you have
552 | permission to link or combine any covered work with a work licensed
553 | under version 3 of the GNU Affero General Public License into a single
554 | combined work, and to convey the resulting work. The terms of this
555 | License will continue to apply to the part which is the covered work,
556 | but the special requirements of the GNU Affero General Public License,
557 | section 13, concerning interaction through a network will apply to the
558 | combination as such.
559 |
560 | #### 14. Revised Versions of this License.
561 |
562 | The Free Software Foundation may publish revised and/or new versions
563 | of the GNU General Public License from time to time. Such new versions
564 | will be similar in spirit to the present version, but may differ in
565 | detail to address new problems or concerns.
566 |
567 | Each version is given a distinguishing version number. If the Program
568 | specifies that a certain numbered version of the GNU General Public
569 | License "or any later version" applies to it, you have the option of
570 | following the terms and conditions either of that numbered version or
571 | of any later version published by the Free Software Foundation. If the
572 | Program does not specify a version number of the GNU General Public
573 | License, you may choose any version ever published by the Free
574 | Software Foundation.
575 |
576 | If the Program specifies that a proxy can decide which future versions
577 | of the GNU General Public License can be used, that proxy's public
578 | statement of acceptance of a version permanently authorizes you to
579 | choose that version for the Program.
580 |
581 | Later license versions may give you additional or different
582 | permissions. However, no additional obligations are imposed on any
583 | author or copyright holder as a result of your choosing to follow a
584 | later version.
585 |
586 | #### 15. Disclaimer of Warranty.
587 |
588 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
589 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
590 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
591 | WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
592 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
593 | A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
594 | PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
595 | DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
596 | CORRECTION.
597 |
598 | #### 16. Limitation of Liability.
599 |
600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR
602 | CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
603 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
604 | ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
605 | NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR
606 | LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
607 | TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
608 | PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
609 |
610 | #### 17. Interpretation of Sections 15 and 16.
611 |
612 | If the disclaimer of warranty and limitation of liability provided
613 | above cannot be given local legal effect according to their terms,
614 | reviewing courts shall apply local law that most closely approximates
615 | an absolute waiver of all civil liability in connection with the
616 | Program, unless a warranty or assumption of liability accompanies a
617 | copy of the Program in return for a fee.
618 |
619 | END OF TERMS AND CONDITIONS
620 |
621 | ### How to Apply These Terms to Your New Programs
622 |
623 | If you develop a new program, and you want it to be of the greatest
624 | possible use to the public, the best way to achieve this is to make it
625 | free software which everyone can redistribute and change under these
626 | terms.
627 |
628 | To do so, attach the following notices to the program. It is safest to
629 | attach them to the start of each source file to most effectively state
630 | the exclusion of warranty; and each file should have at least the
631 | "copyright" line and a pointer to where the full notice is found.
632 |
633 |
634 | Copyright (C)
635 |
636 | This program is free software: you can redistribute it and/or modify
637 | it under the terms of the GNU General Public License as published by
638 | the Free Software Foundation, either version 3 of the License, or
639 | (at your option) any later version.
640 |
641 | This program is distributed in the hope that it will be useful,
642 | but WITHOUT ANY WARRANTY; without even the implied warranty of
643 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
644 | GNU General Public License for more details.
645 |
646 | You should have received a copy of the GNU General Public License
647 | along with this program. If not, see .
648 |
649 | Also add information on how to contact you by electronic and paper
650 | mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands \`show w' and \`show c' should show the
661 | appropriate parts of the General Public License. Of course, your
662 | program's commands might be different; for a GUI interface, you would
663 | use an "about box".
664 |
665 | You should also get your employer (if you work as a programmer) or
666 | school, if any, to sign a "copyright disclaimer" for the program, if
667 | necessary. For more information on this, and how to apply and follow
668 | the GNU GPL, see .
669 |
670 | The GNU General Public License does not permit incorporating your
671 | program into proprietary programs. If your program is a subroutine
672 | library, you may consider it more useful to permit linking proprietary
673 | applications with the library. If this is what you want to do, use the
674 | GNU Lesser General Public License instead of this License. But first,
675 | please read .
676 |
--------------------------------------------------------------------------------