629 lines
22 KiB
Python
629 lines
22 KiB
Python
# Copyright 2010 Dirk Holtwick, holtwick.it
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
from __future__ import annotations
|
|
|
|
import contextlib
|
|
import logging
|
|
import re
|
|
from copy import copy
|
|
from typing import Any
|
|
|
|
import arabic_reshaper
|
|
import reportlab
|
|
import reportlab.pdfbase._cidfontdata
|
|
from bidi.algorithm import get_display
|
|
from reportlab.lib.colors import Color, toColor
|
|
from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY, TA_LEFT, TA_RIGHT
|
|
from reportlab.lib.units import cm, inch
|
|
from reportlab.pdfbase import pdfmetrics
|
|
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
|
|
|
|
import xhtml2pdf.default
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
rgb_re = re.compile(
|
|
r"^.*?rgb[a]?[(]([0-9]+).*?([0-9]+).*?([0-9]+)(?:.*?(?:[01]\.(?:[0-9]+)))?[)].*?[ ]*$"
|
|
)
|
|
|
|
# =========================================================================
|
|
# Memoize decorator
|
|
# =========================================================================
|
|
|
|
|
|
class Memoized:
|
|
"""
|
|
A kwargs-aware memoizer, better than the one in python :).
|
|
|
|
Don't pass in too large kwargs, since this turns them into a tuple of
|
|
tuples. Also, avoid mutable types (as usual for memoizers)
|
|
|
|
What this does is to create a dictionnary of {(*parameters):return value},
|
|
and uses it as a cache for subsequent calls to the same method.
|
|
It is especially useful for functions that don't rely on external variables
|
|
and that are called often. It's a perfect match for our getSize etc...
|
|
"""
|
|
|
|
def __init__(self, func) -> None:
|
|
self.cache: dict = {}
|
|
self.func = func
|
|
self.__doc__ = self.func.__doc__ # To avoid great confusion
|
|
self.__name__ = self.func.__name__ # This also avoids great confusion
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
# Make sure the following line is not actually slower than what you're
|
|
# trying to memoize
|
|
args_plus = tuple(kwargs.items())
|
|
key = (args, args_plus)
|
|
try:
|
|
if key not in self.cache:
|
|
res = self.func(*args, **kwargs)
|
|
self.cache[key] = res
|
|
return self.cache[key]
|
|
except TypeError:
|
|
# happens if any of the parameters is a list
|
|
return self.func(*args, **kwargs)
|
|
|
|
|
|
def toList(value: Any, *, cast_tuple: bool = True) -> list:
|
|
cls: tuple[type, ...] = (list, tuple) if cast_tuple else (list,)
|
|
return list(value) if isinstance(value, cls) else [value]
|
|
|
|
|
|
def transform_attrs(obj, keys, container, func, extras=None):
|
|
"""
|
|
Allows to apply one function to set of keys cheching if key is in container,
|
|
also trasform ccs key to report lab keys.
|
|
|
|
extras = Are extra params for func, it will be call like func(*[param1, param2])
|
|
|
|
obj = frag
|
|
keys = [(reportlab, css), ... ]
|
|
container = cssAttr
|
|
"""
|
|
cpextras = extras
|
|
|
|
for reportlab_key, css in keys:
|
|
extras = cpextras
|
|
if extras is None:
|
|
extras = []
|
|
elif not isinstance(extras, list):
|
|
extras = [extras]
|
|
if css in container:
|
|
extras.insert(0, container[css])
|
|
setattr(obj, reportlab_key, func(*extras))
|
|
|
|
|
|
def copy_attrs(obj1, obj2, attrs):
|
|
"""
|
|
Allows copy a list of attributes from object2 to object1.
|
|
Useful for copy ccs attributes to fragment.
|
|
"""
|
|
for attr in attrs:
|
|
value = getattr(obj2, attr) if hasattr(obj2, attr) else None
|
|
if value is None and isinstance(obj2, dict) and attr in obj2:
|
|
value = obj2[attr]
|
|
setattr(obj1, attr, value)
|
|
|
|
|
|
def set_value(obj, attrs, value, *, do_copy=False):
|
|
"""Allows set the same value to a list of attributes."""
|
|
for attr in attrs:
|
|
if do_copy:
|
|
value = copy(value)
|
|
setattr(obj, attr, value)
|
|
|
|
|
|
@Memoized
|
|
def getColor(value, default=None):
|
|
"""
|
|
Convert to color value.
|
|
This returns a Color object instance from a text bit.
|
|
"""
|
|
if value is None:
|
|
return None
|
|
if isinstance(value, Color):
|
|
return value
|
|
value = str(value).strip().lower()
|
|
if value in {"transparent", "none"}:
|
|
return default
|
|
if value in COLOR_BY_NAME:
|
|
return COLOR_BY_NAME[value]
|
|
if value.startswith("#") and len(value) == 4:
|
|
value = "#" + value[1] + value[1] + value[2] + value[2] + value[3] + value[3]
|
|
elif rgb_re.search(value):
|
|
# e.g., value = "<css function: rgb(153, 51, 153)>", go figure:
|
|
r, g, b = (int(x) for x in rgb_re.search(value).groups())
|
|
value = f"#{r:02x}{g:02x}{b:02x}"
|
|
else:
|
|
# Shrug
|
|
pass
|
|
|
|
return toColor(value, default) # Calling the reportlab function
|
|
|
|
|
|
def getBorderStyle(value, default=None):
|
|
if value and (str(value).lower() not in ("none", "hidden")):
|
|
return value
|
|
return default
|
|
|
|
|
|
MM: float = cm / 10.0
|
|
DPI96: float = 1.0 / 96.0 * inch
|
|
|
|
ABSOLUTE_SIZE_TABLE: dict[str, float] = {
|
|
"1": 50.0 / 100.0,
|
|
"xx-small": 50.0 / 100.0,
|
|
"x-small": 50.0 / 100.0,
|
|
"2": 75.0 / 100.0,
|
|
"small": 75.0 / 100.0,
|
|
"3": 1.0,
|
|
"medium": 1.0,
|
|
"4": 125.0 / 100.0,
|
|
"large": 125.0 / 100.0,
|
|
"5": 150.0 / 100.0,
|
|
"x-large": 150.0 / 100.0,
|
|
"6": 175.0 / 100.0,
|
|
"xx-large": 175.0 / 100.0,
|
|
"7": 200.0 / 100.0,
|
|
"xxx-large": 200.0 / 100.0,
|
|
}
|
|
|
|
RELATIVE_SIZE_TABLE: dict[str, float] = {
|
|
"larger": 1.25,
|
|
"smaller": 0.75,
|
|
"+4": 200.0 / 100.0,
|
|
"+3": 175.0 / 100.0,
|
|
"+2": 150.0 / 100.0,
|
|
"+1": 125.0 / 100.0,
|
|
"-1": 75.0 / 100.0,
|
|
"-2": 50.0 / 100.0,
|
|
"-3": 25.0 / 100.0,
|
|
}
|
|
|
|
MIN_FONT_SIZE: float = 1.0
|
|
|
|
|
|
@Memoized
|
|
def getSize(
|
|
value: str | float | list | tuple,
|
|
relative=0,
|
|
base: int | None = None,
|
|
default: float = 0.0,
|
|
) -> float:
|
|
"""
|
|
Converts strings to standard sizes.
|
|
That is the function taking a string of CSS size ('12pt', '1cm' and so on)
|
|
and converts it into a float in a standard unit (in our case, points).
|
|
|
|
>>> getSize('12pt')
|
|
12.0
|
|
>>> getSize('1cm')
|
|
28.346456692913385
|
|
"""
|
|
try:
|
|
original = value
|
|
if value is None:
|
|
return relative
|
|
if isinstance(value, float):
|
|
return value
|
|
if isinstance(value, int):
|
|
return float(value)
|
|
if isinstance(value, (tuple, list)):
|
|
value = "".join(value)
|
|
value = str(value).strip().lower().replace(",", ".")
|
|
if value.endswith("cm"):
|
|
return float(value[:-2].strip()) * cm
|
|
if value.endswith("mm"):
|
|
return float(value[:-2].strip()) * MM # 1MM = 0.1cm
|
|
if value.endswith("in"):
|
|
return float(value[:-2].strip()) * inch # 1pt == 1/72inch
|
|
if value.endswith("pt"):
|
|
return float(value[:-2].strip())
|
|
if value.endswith("pc"):
|
|
return float(value[:-2].strip()) * 12.0 # 1pc == 12pt
|
|
if value.endswith("px"):
|
|
# XXX W3C says, use 96pdi
|
|
# http://www.w3.org/TR/CSS21/syndata.html#length-units
|
|
return float(value[:-2].strip()) * DPI96
|
|
if value in ("none", "0", "0.0", "auto"):
|
|
return 0.0
|
|
if relative:
|
|
if value.endswith("rem"): # XXX
|
|
# 1rem = 1 * fontSize
|
|
return float(value[:-3].strip()) * relative
|
|
if value.endswith("em"): # XXX
|
|
# 1em = 1 * fontSize
|
|
return float(value[:-2].strip()) * relative
|
|
if value.endswith("ex"): # XXX
|
|
# 1ex = 1/2 fontSize
|
|
return float(value[:-2].strip()) * (relative / 2.0)
|
|
if value.endswith("%"):
|
|
# 1% = (fontSize * 1) / 100
|
|
return (relative * float(value[:-1].strip())) / 100.0
|
|
if value in {"normal", "inherit"}:
|
|
return relative
|
|
if value in RELATIVE_SIZE_TABLE:
|
|
if base:
|
|
return max(MIN_FONT_SIZE, base * RELATIVE_SIZE_TABLE[value])
|
|
return max(MIN_FONT_SIZE, relative * RELATIVE_SIZE_TABLE[value])
|
|
if value in ABSOLUTE_SIZE_TABLE:
|
|
if base:
|
|
return max(MIN_FONT_SIZE, base * ABSOLUTE_SIZE_TABLE[value])
|
|
return max(MIN_FONT_SIZE, relative * ABSOLUTE_SIZE_TABLE[value])
|
|
return max(MIN_FONT_SIZE, relative * float(value))
|
|
try:
|
|
value = float(value)
|
|
except ValueError:
|
|
log.warning("getSize: Not a float %r", value)
|
|
return default # value = 0
|
|
return max(0, value)
|
|
except Exception:
|
|
log.warning("getSize %r %r", original, relative, exc_info=True)
|
|
return default
|
|
|
|
|
|
@Memoized
|
|
def getCoords(x, y, w, h, pagesize):
|
|
"""
|
|
As a stupid programmer I like to use the upper left
|
|
corner of the document as the 0,0 coords therefore
|
|
we need to do some fancy calculations.
|
|
"""
|
|
# ~ print pagesize
|
|
ax, ay = pagesize
|
|
if x < 0:
|
|
x = ax + x
|
|
if y < 0:
|
|
y = ay + y
|
|
if w is not None and h is not None:
|
|
if w <= 0:
|
|
w = ax - x + w
|
|
if h <= 0:
|
|
h = ay - y + h
|
|
return x, (ay - y - h), w, h
|
|
return x, (ay - y)
|
|
|
|
|
|
@Memoized
|
|
def getBox(box, pagesize):
|
|
"""
|
|
Parse sizes by corners in the form:
|
|
<X-Left> <Y-Upper> <Width> <Height>
|
|
The last to values with negative values are interpreted as offsets form
|
|
the right and lower border.
|
|
"""
|
|
box = str(box).split()
|
|
if len(box) != 4:
|
|
msg = "box not defined right way"
|
|
raise RuntimeError(msg)
|
|
x, y, w, h = (getSize(pos) for pos in box)
|
|
return getCoords(x, y, w, h, pagesize)
|
|
|
|
|
|
def getFrameDimensions(
|
|
data, page_width: float, page_height: float
|
|
) -> tuple[float, float, float, float]:
|
|
"""
|
|
Calculate dimensions of a frame.
|
|
|
|
Returns left, top, width and height of the frame in points.
|
|
"""
|
|
box = data.get("-pdf-frame-box", [])
|
|
if len(box) == 4:
|
|
return (getSize(box[0]), getSize(box[1]), getSize(box[2]), getSize(box[3]))
|
|
top = getSize(data.get("top", 0))
|
|
left = getSize(data.get("left", 0))
|
|
bottom = getSize(data.get("bottom", 0))
|
|
right = getSize(data.get("right", 0))
|
|
if "height" in data:
|
|
height = getSize(data["height"])
|
|
if "top" in data:
|
|
top = getSize(data["top"])
|
|
bottom = page_height - (top + height)
|
|
elif "bottom" in data:
|
|
bottom = getSize(data["bottom"])
|
|
top = page_height - (bottom + height)
|
|
if "width" in data:
|
|
width = getSize(data["width"])
|
|
if "left" in data:
|
|
left = getSize(data["left"])
|
|
right = page_width - (left + width)
|
|
elif "right" in data:
|
|
right = getSize(data["right"])
|
|
left = page_width - (right + width)
|
|
top += getSize(data.get("margin-top", 0))
|
|
left += getSize(data.get("margin-left", 0))
|
|
bottom += getSize(data.get("margin-bottom", 0))
|
|
right += getSize(data.get("margin-right", 0))
|
|
|
|
width = page_width - (left + right)
|
|
height = page_height - (top + bottom)
|
|
return left, top, width, height
|
|
|
|
|
|
@Memoized
|
|
def getPos(position, pagesize):
|
|
"""Pair of coordinates."""
|
|
position = str(position).split()
|
|
if len(position) != 2:
|
|
msg = "position not defined right way"
|
|
raise RuntimeError(msg)
|
|
x, y = (getSize(pos) for pos in position)
|
|
return getCoords(x, y, None, None, pagesize)
|
|
|
|
|
|
def getBool(s):
|
|
"""Is it a boolean?."""
|
|
return str(s).lower() in ("y", "yes", "1", "true")
|
|
|
|
|
|
def getFloat(s):
|
|
with contextlib.suppress(Exception):
|
|
return float(s)
|
|
|
|
|
|
ALIGNMENTS = {
|
|
"left": TA_LEFT,
|
|
"center": TA_CENTER,
|
|
"middle": TA_CENTER,
|
|
"right": TA_RIGHT,
|
|
"justify": TA_JUSTIFY,
|
|
}
|
|
|
|
|
|
def getAlign(value, default=TA_LEFT):
|
|
return ALIGNMENTS.get(str(value).lower(), default)
|
|
|
|
|
|
_rx_datauri = re.compile(
|
|
"^data:(?P<mime>[a-z]+/[a-z]+);base64,(?P<data>.*)$", re.M | re.DOTALL
|
|
)
|
|
|
|
COLOR_BY_NAME = {
|
|
"activeborder": Color(212, 208, 200),
|
|
"activecaption": Color(10, 36, 106),
|
|
"aliceblue": Color(0.941176, 0.972549, 1),
|
|
"antiquewhite": Color(0.980392, 0.921569, 0.843137),
|
|
"appworkspace": Color(128, 128, 128),
|
|
"aqua": Color(0, 1, 1),
|
|
"aquamarine": Color(0.498039, 1, 0.831373),
|
|
"azure": Color(0.941176, 1, 1),
|
|
"background": Color(58, 110, 165),
|
|
"beige": Color(0.960784, 0.960784, 0.862745),
|
|
"bisque": Color(1, 0.894118, 0.768627),
|
|
"black": Color(0, 0, 0),
|
|
"blanchedalmond": Color(1, 0.921569, 0.803922),
|
|
"blue": Color(0, 0, 1),
|
|
"blueviolet": Color(0.541176, 0.168627, 0.886275),
|
|
"brown": Color(0.647059, 0.164706, 0.164706),
|
|
"burlywood": Color(0.870588, 0.721569, 0.529412),
|
|
"buttonface": Color(212, 208, 200),
|
|
"buttonhighlight": Color(255, 255, 255),
|
|
"buttonshadow": Color(128, 128, 128),
|
|
"buttontext": Color(0, 0, 0),
|
|
"cadetblue": Color(0.372549, 0.619608, 0.627451),
|
|
"captiontext": Color(255, 255, 255),
|
|
"chartreuse": Color(0.498039, 1, 0),
|
|
"chocolate": Color(0.823529, 0.411765, 0.117647),
|
|
"coral": Color(1, 0.498039, 0.313725),
|
|
"cornflowerblue": Color(0.392157, 0.584314, 0.929412),
|
|
"cornsilk": Color(1, 0.972549, 0.862745),
|
|
"crimson": Color(0.862745, 0.078431, 0.235294),
|
|
"cyan": Color(0, 1, 1),
|
|
"darkblue": Color(0, 0, 0.545098),
|
|
"darkcyan": Color(0, 0.545098, 0.545098),
|
|
"darkgoldenrod": Color(0.721569, 0.52549, 0.043137),
|
|
"darkgray": Color(0.662745, 0.662745, 0.662745),
|
|
"darkgreen": Color(0, 0.392157, 0),
|
|
"darkgrey": Color(0.662745, 0.662745, 0.662745),
|
|
"darkkhaki": Color(0.741176, 0.717647, 0.419608),
|
|
"darkmagenta": Color(0.545098, 0, 0.545098),
|
|
"darkolivegreen": Color(0.333333, 0.419608, 0.184314),
|
|
"darkorange": Color(1, 0.54902, 0),
|
|
"darkorchid": Color(0.6, 0.196078, 0.8),
|
|
"darkred": Color(0.545098, 0, 0),
|
|
"darksalmon": Color(0.913725, 0.588235, 0.478431),
|
|
"darkseagreen": Color(0.560784, 0.737255, 0.560784),
|
|
"darkslateblue": Color(0.282353, 0.239216, 0.545098),
|
|
"darkslategray": Color(0.184314, 0.309804, 0.309804),
|
|
"darkslategrey": Color(0.184314, 0.309804, 0.309804),
|
|
"darkturquoise": Color(0, 0.807843, 0.819608),
|
|
"darkviolet": Color(0.580392, 0, 0.827451),
|
|
"deeppink": Color(1, 0.078431, 0.576471),
|
|
"deepskyblue": Color(0, 0.74902, 1),
|
|
"dimgray": Color(0.411765, 0.411765, 0.411765),
|
|
"dimgrey": Color(0.411765, 0.411765, 0.411765),
|
|
"dodgerblue": Color(0.117647, 0.564706, 1),
|
|
"firebrick": Color(0.698039, 0.133333, 0.133333),
|
|
"floralwhite": Color(1, 0.980392, 0.941176),
|
|
"forestgreen": Color(0.133333, 0.545098, 0.133333),
|
|
"fuchsia": Color(1, 0, 1),
|
|
"gainsboro": Color(0.862745, 0.862745, 0.862745),
|
|
"ghostwhite": Color(0.972549, 0.972549, 1),
|
|
"gold": Color(1, 0.843137, 0),
|
|
"goldenrod": Color(0.854902, 0.647059, 0.12549),
|
|
"gray": Color(0.501961, 0.501961, 0.501961),
|
|
"graytext": Color(128, 128, 128),
|
|
"green": Color(0, 0.501961, 0),
|
|
"greenyellow": Color(0.678431, 1, 0.184314),
|
|
"grey": Color(0.501961, 0.501961, 0.501961),
|
|
"highlight": Color(10, 36, 106),
|
|
"highlighttext": Color(255, 255, 255),
|
|
"honeydew": Color(0.941176, 1, 0.941176),
|
|
"hotpink": Color(1, 0.411765, 0.705882),
|
|
"inactiveborder": Color(212, 208, 200),
|
|
"inactivecaption": Color(128, 128, 128),
|
|
"inactivecaptiontext": Color(212, 208, 200),
|
|
"indianred": Color(0.803922, 0.360784, 0.360784),
|
|
"indigo": Color(0.294118, 0, 0.509804),
|
|
"infobackground": Color(255, 255, 225),
|
|
"infotext": Color(0, 0, 0),
|
|
"ivory": Color(1, 1, 0.941176),
|
|
"khaki": Color(0.941176, 0.901961, 0.54902),
|
|
"lavender": Color(0.901961, 0.901961, 0.980392),
|
|
"lavenderblush": Color(1, 0.941176, 0.960784),
|
|
"lawngreen": Color(0.486275, 0.988235, 0),
|
|
"lemonchiffon": Color(1, 0.980392, 0.803922),
|
|
"lightblue": Color(0.678431, 0.847059, 0.901961),
|
|
"lightcoral": Color(0.941176, 0.501961, 0.501961),
|
|
"lightcyan": Color(0.878431, 1, 1),
|
|
"lightgoldenrodyellow": Color(0.980392, 0.980392, 0.823529),
|
|
"lightgray": Color(0.827451, 0.827451, 0.827451),
|
|
"lightgreen": Color(0.564706, 0.933333, 0.564706),
|
|
"lightgrey": Color(0.827451, 0.827451, 0.827451),
|
|
"lightpink": Color(1, 0.713725, 0.756863),
|
|
"lightsalmon": Color(1, 0.627451, 0.478431),
|
|
"lightseagreen": Color(0.12549, 0.698039, 0.666667),
|
|
"lightskyblue": Color(0.529412, 0.807843, 0.980392),
|
|
"lightslategray": Color(0.466667, 0.533333, 0.6),
|
|
"lightslategrey": Color(0.466667, 0.533333, 0.6),
|
|
"lightsteelblue": Color(0.690196, 0.768627, 0.870588),
|
|
"lightyellow": Color(1, 1, 0.878431),
|
|
"lime": Color(0, 1, 0),
|
|
"limegreen": Color(0.196078, 0.803922, 0.196078),
|
|
"linen": Color(0.980392, 0.941176, 0.901961),
|
|
"magenta": Color(1, 0, 1),
|
|
"maroon": Color(0.501961, 0, 0),
|
|
"mediumaquamarine": Color(0.4, 0.803922, 0.666667),
|
|
"mediumblue": Color(0, 0, 0.803922),
|
|
"mediumorchid": Color(0.729412, 0.333333, 0.827451),
|
|
"mediumpurple": Color(0.576471, 0.439216, 0.858824),
|
|
"mediumseagreen": Color(0.235294, 0.701961, 0.443137),
|
|
"mediumslateblue": Color(0.482353, 0.407843, 0.933333),
|
|
"mediumspringgreen": Color(0, 0.980392, 0.603922),
|
|
"mediumturquoise": Color(0.282353, 0.819608, 0.8),
|
|
"mediumvioletred": Color(0.780392, 0.082353, 0.521569),
|
|
"menu": Color(212, 208, 200),
|
|
"menutext": Color(0, 0, 0),
|
|
"midnightblue": Color(0.098039, 0.098039, 0.439216),
|
|
"mintcream": Color(0.960784, 1, 0.980392),
|
|
"mistyrose": Color(1, 0.894118, 0.882353),
|
|
"moccasin": Color(1, 0.894118, 0.709804),
|
|
"navajowhite": Color(1, 0.870588, 0.678431),
|
|
"navy": Color(0, 0, 0.501961),
|
|
"oldlace": Color(0.992157, 0.960784, 0.901961),
|
|
"olive": Color(0.501961, 0.501961, 0),
|
|
"olivedrab": Color(0.419608, 0.556863, 0.137255),
|
|
"orange": Color(1, 0.647059, 0),
|
|
"orangered": Color(1, 0.270588, 0),
|
|
"orchid": Color(0.854902, 0.439216, 0.839216),
|
|
"palegoldenrod": Color(0.933333, 0.909804, 0.666667),
|
|
"palegreen": Color(0.596078, 0.984314, 0.596078),
|
|
"paleturquoise": Color(0.686275, 0.933333, 0.933333),
|
|
"palevioletred": Color(0.858824, 0.439216, 0.576471),
|
|
"papayawhip": Color(1, 0.937255, 0.835294),
|
|
"peachpuff": Color(1, 0.854902, 0.72549),
|
|
"peru": Color(0.803922, 0.521569, 0.247059),
|
|
"pink": Color(1, 0.752941, 0.796078),
|
|
"plum": Color(0.866667, 0.627451, 0.866667),
|
|
"powderblue": Color(0.690196, 0.878431, 0.901961),
|
|
"purple": Color(0.501961, 0, 0.501961),
|
|
"red": Color(1, 0, 0),
|
|
"rosybrown": Color(0.737255, 0.560784, 0.560784),
|
|
"royalblue": Color(0.254902, 0.411765, 0.882353),
|
|
"saddlebrown": Color(0.545098, 0.270588, 0.07451),
|
|
"salmon": Color(0.980392, 0.501961, 0.447059),
|
|
"sandybrown": Color(0.956863, 0.643137, 0.376471),
|
|
"scrollbar": Color(212, 208, 200),
|
|
"seagreen": Color(0.180392, 0.545098, 0.341176),
|
|
"seashell": Color(1, 0.960784, 0.933333),
|
|
"sienna": Color(0.627451, 0.321569, 0.176471),
|
|
"silver": Color(0.752941, 0.752941, 0.752941),
|
|
"skyblue": Color(0.529412, 0.807843, 0.921569),
|
|
"slateblue": Color(0.415686, 0.352941, 0.803922),
|
|
"slategray": Color(0.439216, 0.501961, 0.564706),
|
|
"slategrey": Color(0.439216, 0.501961, 0.564706),
|
|
"snow": Color(1, 0.980392, 0.980392),
|
|
"springgreen": Color(0, 1, 0.498039),
|
|
"steelblue": Color(0.27451, 0.509804, 0.705882),
|
|
"tan": Color(0.823529, 0.705882, 0.54902),
|
|
"teal": Color(0, 0.501961, 0.501961),
|
|
"thistle": Color(0.847059, 0.74902, 0.847059),
|
|
"threeddarkshadow": Color(64, 64, 64),
|
|
"threedface": Color(212, 208, 200),
|
|
"threedhighlight": Color(255, 255, 255),
|
|
"threedlightshadow": Color(212, 208, 200),
|
|
"threedshadow": Color(128, 128, 128),
|
|
"tomato": Color(1, 0.388235, 0.278431),
|
|
"turquoise": Color(0.25098, 0.878431, 0.815686),
|
|
"violet": Color(0.933333, 0.509804, 0.933333),
|
|
"wheat": Color(0.960784, 0.870588, 0.701961),
|
|
"white": Color(1, 1, 1),
|
|
"whitesmoke": Color(0.960784, 0.960784, 0.960784),
|
|
"window": Color(255, 255, 255),
|
|
"windowframe": Color(0, 0, 0),
|
|
"windowtext": Color(0, 0, 0),
|
|
"yellow": Color(1, 1, 0),
|
|
"yellowgreen": Color(0.603922, 0.803922, 0.196078),
|
|
}
|
|
|
|
|
|
def get_default_asian_font():
|
|
lower_font_list = []
|
|
upper_font_list = []
|
|
|
|
font_dict = copy(reportlab.pdfbase._cidfontdata.defaultUnicodeEncodings)
|
|
fonts = font_dict.keys()
|
|
|
|
for font in fonts:
|
|
upper_font_list.append(font)
|
|
lower_font_list.append(font.lower())
|
|
return {lower_font_list[i]: upper_font_list[i] for i in range(len(lower_font_list))}
|
|
|
|
|
|
def set_asian_fonts(fontname):
|
|
font_dict = copy(reportlab.pdfbase._cidfontdata.defaultUnicodeEncodings)
|
|
fonts = font_dict.keys()
|
|
if fontname in fonts:
|
|
pdfmetrics.registerFont(UnicodeCIDFont(fontname))
|
|
|
|
|
|
def detect_language(name):
|
|
asian_language_list = xhtml2pdf.default.DEFAULT_LANGUAGE_LIST
|
|
if name in asian_language_list:
|
|
return name
|
|
return None
|
|
|
|
|
|
def arabic_format(text, language):
|
|
# Note: right now all of the languages are treated the same way.
|
|
# But maybe in the future we have to for example implement something
|
|
# for "hebrew" that isn't used in "arabic"
|
|
if detect_language(language) in (
|
|
"arabic",
|
|
"hebrew",
|
|
"persian",
|
|
"urdu",
|
|
"pashto",
|
|
"sindhi",
|
|
):
|
|
ar = arabic_reshaper.reshape(text)
|
|
return get_display(ar)
|
|
return None
|
|
|
|
|
|
def frag_text_language_check(context, frag_text):
|
|
if hasattr(context, "language"):
|
|
language = context.__getattribute__("language")
|
|
detect_language_result = arabic_format(frag_text, language)
|
|
if detect_language_result:
|
|
return detect_language_result
|
|
return None
|
|
return None
|
|
|
|
|
|
class ImageWarning(Exception): # noqa: N818
|
|
pass
|