Flat is a library for creating and manipulating digital forms of fine arts. Its aim is to enable experimentation with and testing of unpredictable or automated processes, to inspect the beginning of the "new".
It grew out of the needs for generative design, architecture and art. The concept of "design" is more of a subject of study yet to be delved into, hence the fitter term for subtitle is "infrastructure".
It is written in pure Python and distributed under a liberal license.
- Graphic formats
- PNG, JPEG, PDF, SVG, OpenType (both TrueType and PostScript outlines), STL
- Color spaces
- grayscale, grayscale + alpha, RGB, RGBA, CMYK, spot colors, overprint
- Image manipulation
- resizing, blurring, dithering, ...
- Image synthesis
- Bezier path rasterization, BVH accelerated path tracing with explicit emitters and stratified sampling
- Vector graphic primitives
- line, polyline, ..., path, text, outlines, groups and units
- Typography
- kerning, greedy line breaking, threaded text frames
- Computational geometry
- Boolean operation on polygons
- Data visualization
- tree layout, rectangular bin packing
- Barcode generation
- EAN13 symbology
Flat library consists of three (slightly overlapping) parts: image, document and scene. An image is basically a container of pixels and a color kind. There are a few methods which operate over those pixels, such as "blur" or "put". It is possible to create completely new image, by opening a file or by "rasterizing" a page of document.
Document is then assembled from pages, and each page holds number of "placed" items, both of which can be exported, too. Here it also makes sense to introduce other entities: colors, shapes and strikes. There is support for following colors or color spaces, if you will: the usual ones (grayscale, RGB, CMYK, ...), spot colors that can be used for controlling the application of special colorants and "overprint", which allows for printing of a graphic figure without erasing anything below it. A shape includes both graphical properties such as stroke width or miter limit and means of creating items with said properties that are to be "placed" into a page, for example "line" or "circle". Strike is a similar combination of text attributes (font, size, color, ...) and a way of constructing text "spans". A span likewise connects text string to text attributes, one or more spans can form a "paragraph", which in turn may form "text" or "outlines". Outlines are similar to texts but they use paths of glyph outlines, instead of characters. One additional thing to note is that placed texts or outlines may be linked into a story or "chain" of blocks, making the text gradually "flow" from one text frame to another. Any of the items may be placed into a "group" as well.
Finally, a scene is made of (possibly light emitting) materials, meshes built of triangular faces defined by "triplets" of 3D vertices and a camera.
from flat import rgb, font, shape, strike, document
red = rgb(255, 0, 0)
lato = font.open('Lato-Reg.otf')
figure = shape().stroke(red).width(2.5)
headline = strike(lato).color(red).size(20, 24)
d = document(100, 100, 'mm')
p = d.addpage()
p.place(figure.circle(50, 50, 20))
p.place(headline.text('Hello world!')).frame(10, 10, 80, 80)
p.image(kind='rgb').png('hello.png')
p.svg('hello.svg')
d.pdf('hello.pdf')
Short commentary:
We first prepared some invariants which we are going to use later, like the body typeface, some RGB color or a typeface we opened from a font file. One can think of shape
and strike
as of customizable factories which produce more concrete objects, for example lines or spans of text.
Next is the basic document hierarchy with just one page that can have items be placed into. The origin of coordinate system (0, 0) is at the top left corner and most of the time the default unit is "points" (1 inch = 72 points). A placed item may have some additional properties as position or frame
. The latter is used to define the boundaries inside whose the text may run. As Flat currently lacks any kind of color management, we need to use the same color space for rasterizing the page into a PNG file. To access a page at any time one can simply keep a reference to it (p
). Lastly, note that PDF is one of the few graphic formats which can hold multiple pages.
There is also a public repository hosting additional examples.
image.open(
path
)
-
- Open an image located at
path
. Supported formats are JPEG and PNG.
- Open an image located at
-
image(
width, height, kind='rgb'
)
-
- Create an image
width
byheight
pixels in resolution, wherekind
can be one of:'g'
(grayscale),'ga'
(grayscale + alpha),'rgb'
,'rgba'
,'cmyk'
.
- Create an image
copy()
- Return a deep copy of the image.
get(
x, y
)
- Return the color values of pixel at
x
,y
.
- Return the color values of pixel at
put(
x, y, components
)
- Set the color of pixel at
x
,y
tocomponents
.
- Set the color of pixel at
fill(
components
)
- Fill the image with solid color of
components
.
- Fill the image with solid color of
white()
- Fill the image with solid white.
black()
- Fill the image with solid black.
blit(
x, y, source
)
- Copy a region from
source
. Position of the region isx
,y
in this image,0
,0
in the source. Size of the region is the size of the source, cropping it to size of this image as necessary.
- Copy a region from
crop(
x, y, width, height
)
- Crop the image to frame with origin at
x
,y
and sizewidth
,height
. The result will not enlarge beyond original size.
- Crop the image to frame with origin at
flip(
horizontal, vertical
)
- Flip the image horizontally and/or vertically.
transpose()
- Flip the image over the diagonal.
rotate(
clockwise
)
- Rotate the image by 90 degrees clockwise if
clockwise
isTrue
, anti-clockwise otherwise.
- Rotate the image by 90 degrees clockwise if
resize(
width=0, height=0, interpolation='bicubic'
)
- Resize the image to
width
byheight
, whereinterpolation
can be one of:'nearest'
,'bicubic'
,'lanczos'
. Nearest-neighbor is fastest kernel and produces "pixelated" look when upsizing, bicubic is good general-purpose filter, Lanczos resampling preserves most detail and is slowest of the three.0
width or height maintains the aspect ratio.
- Resize the image to
rescale(
factor, interpolation='bicubic'
)
- Similar to
resize
but usesscale
factor to calculate new dimensions.
- Similar to
blur(
radius
)
- Blur the image with Gaussian filter kernel.
dither(
levels=2
)
- Reduce the number of grayscale intensities to
levels
using Burkes dithering.
- Reduce the number of grayscale intensities to
gamma(
value
)
- Perform gamma correction of
value
on the image.
- Perform gamma correction of
invert()
- Invert color of the image.
png(
path='', optimized=False
)
- Return the image serialized into PNG format. If
path
is set, save it as well. Improve the compression by settingoptimized
toTrue
.
- Return the image serialized into PNG format. If
jpeg(
path='', quality=95
)
- Return the image serialized into JPEG format. If
path
is set, save it as well. Higher (up to100
)quality
lowers the perceptible loss in image quality but increases the storage size.
- Return the image serialized into JPEG format. If
-
placedimage
-
- Don't call directly. Use
page.place()
instead.
- Don't call directly. Use
position(
x, y
)
- Move the placed image to
x
,y
.
- Move the placed image to
frame(
x, y, width, height
)
- Move the placed image to
x
,y
and resize it towidth
,height
.
- Move the placed image to
fitwidth(
width
)
- Proportionally scale the placed image to match
width
.
- Proportionally scale the placed image to match
fitheight(
height
)
- Proportionally scale the placed image to match
height
.
- Proportionally scale the placed image to match
-
raw(
width, height
)
-
- Create a so called "raw" RGB image that comprises of floating-point intensities.
put(
x, y, r, g, b
)
- Set the color of pixel at
x
,y
tor
,g
,b
.
- Set the color of pixel at
tonemapped(
key=0.18, white=1.0
)
- Return an
image
with reduced dynamic range of integer values 0-255, wherekey
indicates whether the scene is subjectively light or dark, typically varying from0.18
to0.4
, andwhite
is the smallest luminance mapped to pure white.
- Return an
-
page
-
- Don't call directly. Use
document.addpage()
instead.
- Don't call directly. Use
meta(
title
)
- Set metadata about the page, such as
title
.
- Set metadata about the page, such as
size(
width, height, units='mm'
)
- Set the default document size to
width
byheight
, whereunits
can be one of:'pt'
,'mm'
,'cm'
,'in'
.
- Set the default document size to
place(
item
)
- Place an
item
on the page.
- Place an
chain(
block
)
- Add new text block to the
block
, enabling its text to flow along the linked blocks. A chain eventually eliminatesoverflow
.
- Add new text block to the
svg(
path='', compress=False
)
- Return the page serialized into SVG format. If
path
is set, save it as well. Reduce size by settingcompress
toTrue
(currently not implemented).
- Return the page serialized into SVG format. If
image(
ppi=72, kind='g'
)
- Return the page rasterized at
ppi
(pixels per inch) intoimage
ofkind
.
- Return the page rasterized at
-
document.open(
path
)
-
- Open a document located at
path
. Currently not implemented.
- Open a document located at
-
document(
width=210.0, height=297.0, units='mm'
)
-
- Create a document with dimensions of
width
byheight
units
.
- Create a document with dimensions of
meta(
title
)
- Set metadata about the document, such as
title
.
- Set metadata about the document, such as
size(
width, height, units='mm'
)
- Set dimensions of the document to
width
byheight
units
.
- Set dimensions of the document to
addpage()
- Create and add one page to the document and return it.
pdf(
path='', compress=False, bleed=False, cropmarks=False
)
- Return the document serialized into PDF. If
path
is set, save it as well. Reduce size by settingcompress
toTrue
(currently not implemented), includebleed
orcropmarks
by setting the arguments toTrue
.
- Return the document serialized into PDF. If
-
mesh.openstl(
path
)
-
- Open an STL mesh located at
path
.
- Open an STL mesh located at
-
mesh(
triplets
)
-
- Create a mesh with
triplets
of triangular face vertices. Each vertex is a triplet of x, y, z coordinates.
- Create a mesh with
stl(
path=''
)
- Return the mesh serialized into STL format. If
path
is set, save it as well.
- Return the mesh serialized into STL format. If
-
diffuse(
reflectance, emittance=None
)
-
- Create a diffuse material with
reflectance
andemittance
, each of being an RGB floating-point triplet.
- Create a diffuse material with
-
scene()
-
- Create empty scene.
environment(
sky, ground
)
- Set the
sky
andground
emittance, each of being an RGB floating-point triplet.
- Set the
camera(
origin, target, length=50.0
)
- Point the camera from
origin
totarget
, each of being an 3D point coordinate. Set focal length tolength
in millimetres.
- Point the camera from
clear()
- Remove all items from the scene.
add(
mesh, material
)
- Add
mesh
combined withmaterial
to the scene.
- Add
render(
width, height, samples=10, multiprocessing=True, info=True
)
- Render the scene to
raw
image with sizewidth
byheight
pixels usingsamples
×samples
number of path tracing samples. To use all available cores setmultiprocessing
toTrue
and to report rendering progress setinfo
toTrue
.
- Render the scene to
-
gray(
intensity
)
-
- Create a grayscale color with
intensity
.0
corresponds to black,255
to white.
- Create a grayscale color with
-
ga(
g, a
)
-
- Create a grayscale color with intensity
g
and alphaa
.0
corresponds to black/transparent,255
to white/opaque.
- Create a grayscale color with intensity
-
rgb(
r, g, b
)
-
- Create an RGB color with intensities
r
,g
,b
.0
corresponds to absence of component,255
to maximum intensity.
- Create an RGB color with intensities
-
rgba(
r, g, b, a
)
-
- Create an RGBA color with intensities
r
,g
,b
and alphaa
.0
corresponds to absence of component/coverage,255
to maximum intensity/opacity.
- Create an RGBA color with intensities
-
cmyk(
c, m, y, k
)
-
- Create a CMYK color with tints
c
,m
,y
,k
.0
denotes the absence of colorant,100
means maximum concentration.
- Create a CMYK color with tints
-
spot(
name, fallback
)
-
- Create a spot color with
name
and CMYKfallback
used in case of absence of colorant in output device.
- Create a spot color with
thinned(
tint
)
- Duplicate the color and sets the amount of application of its colorant to
tint
.0
denotes the absence of colorant,100
means maximum concentration.
- Duplicate the color and sets the amount of application of its colorant to
-
overprint(
color
)
-
- Create a wrapper of
color
which enables overprinting. Argument may be one of:cmyk
,spot
ordevicen
. When printing without overprint a graphic figure erases everything beneath it. With overprint it is possible to stack a layer of paint over preceding layers.
- Create a wrapper of
-
font.open(
path, index=0
)
-
- Open a font file located at
path
.
- Open a font file located at
-
font
-
- Don't call directly, for now. Use
font.open()
instead.
- Don't call directly, for now. Use
-
shape()
-
- Create an abstract shape. Defaults are:
gray(0)
stroke, no fill,'butt'
cap,'miter'
join and4.0
miter limit.
- Create an abstract shape. Defaults are:
stroke(
color
)
- Set the stroke color to
color
.
- Set the stroke color to
fill(
color
)
- Set the fill color to
color
.
- Set the fill color to
nostroke()
- Disable stroke color.
nofill()
- Disable fill color.
width(
value, units='pt'
)
- Set the stroke width to
value
, inunits
.
- Set the stroke width to
cap(
kind
)
- Set the stroke cap to
kind
, may be one of:'butt'
,'round'
,'square'
.
- Set the stroke cap to
join(
kind
)
- Set the stroke join to
kind
, may be one of:'miter'
,'round'
,'bevel'
.
- Set the stroke join to
limit(
value
)
- Set the miter
limit
.
- Set the miter
line(
x0, y0, x1, y1
)
- Create a line from
x0
,y0
tox1
,y1
.
- Create a line from
polyline(
coordinates
)
- Create an open polyline with a sequence of altering x and y
coordinates
.
- Create an open polyline with a sequence of altering x and y
polygon(
coordinates
)
- Create closed polygon with a sequence of altering x and y
coordinates
.
- Create closed polygon with a sequence of altering x and y
rectangle(
x, y, width, height
)
- Create a rectangle with origin
x
,y
and sizewidth
,height
.
- Create a rectangle with origin
circle(
x, y, r
)
- Create a circle with center at
x
,y
and radiusr
.
- Create a circle with center at
ellipse(
x, y, rx, ry
)
- Create an ellipse with center at
x
,y
and horizontal/vertical radiusrx
/ry
.
- Create an ellipse with center at
path(
commands
)
- Create a path constructed out of
commands
. Valid types are:moveto
,lineto
,quato
,curveto
,closepath
.
- Create a path constructed out of
-
placedshape
-
- Don't call directly. Use
page.place()
instead.
- Don't call directly. Use
position(
x, y
)
- Move the placed shape to
x
,y
.
- Move the placed shape to
-
moveto(
x, y
)
-
- Create a command which moves current point to
x
,y
.
- Create a command which moves current point to
-
lineto(
x, y
)
-
- Create a command which draw a line from current point to
x
,y
.
- Create a command which draw a line from current point to
-
quadto(
x1, y1, x, y
)
-
- Create a command which draw a quadratic Bezier curve from current point to
x
,y
, using control pointx1
,y1
.
- Create a command which draw a quadratic Bezier curve from current point to
-
curveto(
x1, y1, x2, y2, x, y
)
-
- Create a command which draw a cubic Bezier curve from current point to
x
,y
, using control pointx1
,y1
andx2
,y2
.
- Create a command which draw a cubic Bezier curve from current point to
-
closepath
-
- Singleton command which closes the current subpath.
-
moveto
,lineto
,quadto
,curveto
,closepath
transform(
a, b, c, d, e, f
)
- Transform the command by matrix
a
,b
,c
,d
,e
,f
.
- Transform the command by matrix
parsepath(
data
)
-
- Parse SVG Path data into sequence of path commands.
-
union(
subject, clipper, perturbation=0.0
)
intersection(
subject, clipper, perturbation=0.0
)
difference(
subject, clipper, perturbation=0.0
)
-
- Return a sequence of path commands resulting from given Boolean opeation on
subject
andclipper
polygons. Scatter the vertices by ±perturbation
amount.
- Return a sequence of path commands resulting from given Boolean opeation on
-
strike(
font
)
-
- Create a strike with
font
. Defaults are:10
pt size,12
pt leading andgray(0)
color.
- Create a strike with
size(
size, leading=0.0, units='pt'
)
- Set the text size and line spacing to
size
andleading
, inunits
. Zero leading calculates a default value according to size.
- Set the text size and line spacing to
color(
color
)
- Set the text color to
color
.
- Set the text color to
width(
string
)
- Measure the width of
string
, in points.
- Measure the width of
span(
string
)
- Create a text span by combining the strike and
string
. Spans may form a paragraph.
- Create a text span by combining the strike and
paragraph(
string
)
- Create a text paragraph with one span by combining the strike and
string
. Paragraphs may form a text.
- Create a text paragraph with one span by combining the strike and
text(
string
)
- Create a text by breaking
string
to paragraphs at newline characters, with each paragraph having one span. Placed texts may be chained to enable text flow. Text yields a sequence of characters tied to the font.
- Create a text by breaking
outlines(
string
)
- Create outlines by breaking
string
to paragraphs at newline characters, with each paragraph having one span. Placed outlines may be chained to enable text flow. Outlines yield a sequence of Bezier paths based on glyph outlines.
- Create outlines by breaking
-
paragraph(
spans
)
-
- Create a paragraph containing
spans
.
- Create a paragraph containing
-
text.open(
path, substitutes
)
,outlines.open(
path, substitutes
)
-
- Open a text file located at
path
and use fontsubstitutes
. Currently not implemented.
- Open a text file located at
-
text(
paragraphs
)
,outlines(
paragraphs
)
-
- Create a text/outlines containing
paragraphs
.
- Create a text/outlines containing
-
placedtext
,placedoutlines
-
- Don't call directly. Use
page.place()
instead. Blocks have infinite sizes by default.
- Don't call directly. Use
position(
x, y
)
- Move the placed text to
x
,y
.
- Move the placed text to
frame(
x, y, width, height
)
- Move the placed text to
x
,y
, resize it towidth
,height
and reflow it along the chain.
- Move the placed text to
overflow()
- Return whether the placed text flows over the frame(s).
lines()
- Return the text content broken into lines according to current layout.
-
group.open(
path
)
-
- Open a vector graphics file located at
path
. Currently not implemented.
- Open a vector graphics file located at
-
group(
units='mm'
)
-
- Create a group with dimensions in
units
.
- Create a group with dimensions in
units(
units='mm'
)
- Set the
units
of the group.
- Set the
place(
item
)
- Place an
item
into the group.
- Place an
chain(
block
)
- Add new text block to the
block
, enabling its text to flow along the linked blocks. A chain eventually eliminatesoverflow
.
- Add new text block to the
-
placedgroup
-
- Don't call directly. Use
page.place()
instead.
- Don't call directly. Use
position(
x, y
)
- Move the placed group to
x
,y
.
- Move the placed group to
scale(
factor
)
- Scale the placed group by
factor
.
- Scale the placed group by
-
ean13(
string
)
-
- Return the widths of the alternating black and white bars of EAN-13 barcode, defined by the 13 decimal digits of
string
.
- Return the widths of the alternating black and white bars of EAN-13 barcode, defined by the 13 decimal digits of
-
tree(
item
)
-
- Create a tree rooted at
item
.
- Create a tree rooted at
add(
item
)
- Add a child (i.e. another tree) with
item
and return it.
- Add a child (i.e. another tree) with
layout()
- Use horizontal layout to position the nodes.
transpose()
- Flip between horizontal and vertical layout.
frame(
x, y, width, height
)
- Move the tree to
x
,y
and resize it towidth
,height
.
- Move the tree to
nodes()
- Return all the tree nodes in depth-first order. Each node has
x
,y
coordinates,parent
,children
and the originalitem
.
- Return all the tree nodes in depth-first order. Each node has
-
binpacker(
width, height
)
-
- Create a rectangular bin packer with dimensions of
width
byheight
.
- Create a rectangular bin packer with dimensions of
pack(
width, height
)
- Return origin coordinates
x
,y
andpacked
flag indicating whether it was possible to add a rectangle ofwidth
,height
size.
- Return origin coordinates
-
view(
data
)
-
- Load
data
into the viewer if called inside of Even, otherwise do nothing.
- Load
-
pip install flat
Since version 0.3, it requires Python 3.
Alternatively, there is Even application which integrates the library, a viewer and Python editor.
The original content of xxyxyz.org can be accessed via Wayback Machine.
Juraj Sukop, [email protected]