Giter Site home page Giter Site logo

epdlib's Introduction

Top Langs Aaron's GitHub stats

Who I Am

I'm a hacker, maker and mender of things. I love an interesting challenge and can't resist being nerd sniped.

More About Me

  • ๐Ÿ”ญ Iโ€™m currently working on building SQL queries for to interface Student Information Systems with Learning Management Systems
  • ๐ŸŒฑ Iโ€™m currently learning SQL
  • ๐Ÿ‘ฏ Iโ€™m looking to collaborate on PaperPi, a quiet internet radio
  • ๐Ÿ“ซ How to reach me:
  • ๐Ÿ“œMy Resume of Projects
  • ๐Ÿ˜„ Pronouns: He/Him

epdlib's People

Contributors

neromtee avatar txoof avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

epdlib's Issues

possible ignore of max_refresh = 30

As it seems, the config parameter max_refresh (which is set to 30 in my case in paperpi.conf) is ignored. Every four changes there is a hard refresh. See this, the last refresh is done right before the video starts:

edit: video doesnt work, see gif at: https://imgur.com/a/6eoObdo
thank god its 2021, you can pause the gif, move forward, etc.

I am using a daemonized setup, defining max_refresh in /etc/default/paperpi.ini

# CONFIG_VERSION=1
[main]
# waveshare display type use HD for IT8951 displays
display_type = HD
# required vcom value for IT8951 screens
vcom = -2.13
# maximum refresh between total screen clear for HD displays
max_refresh = 30

is this intentional or did I do this wrong?

example layout has **many** typos

myLayout = {
        'title': {                       # text only block
            'image': None,               # do not expect an image
            'max_lines': 2,              # number of lines of text
            'width': 1,                  # 1/1 of the width - this stretches the entire width of the display
            'height': 2/3,               # 1/3 of the entire height
            'abs_coordinates': (0, 0),   # this block is the key block that all other blocks will be defined in terms of
            'hcenter': True,             # horizontally center text
            'vcenter': True,             # vertically center text 
            'relative': False,           # this block is not relative to any other. It has an ABSOLUTE position (0, 0)
            'font': './fonts/Font.ttc', # path to font file
            'font_size': None            # Calculate the font size because none was provided
        },
    
        'artist': {
            'image': None,
            'max_lines': 1,
            'width': 1,
            'height': 1/3,
            'abs_coordinates': (0, None),   # X = 0, Y will be calculated
            'hcenter': True,
            'vcenter': True,
            'font': './fonts/Font.ttc',
            'relative': ['artist', 'title'], # use the X postion from abs_coord from `artist` (this block: 0)
                                           # calculate the y position based on the size of `title` block
            
        }
}

Wrong code for rotation by 90 and -90

epdlib/epdlib/Screen.py

Lines 368 to 369 in b1cbb87

if self.rotation == 180 or self.rotation == -90:
image = image.rotate(180)

This will not, I think, give the expected result for 90 degrees, nor for -90.

Since supported rotation was checked earlier, I think this should just be:

            image = image.rotate(self.rotation)

Screen() object hangs forever when trying to init IT8951

When no IT8951 display is connected, the epdlib.Screen(epd='HD') hangs forever.

This is related to how the IT8951 module pulls info from the board.

IT8951/interface.py in update_system_info(self)
     97         Get information about the system, and store it in class attributes
     98         '''
---> 99         self.spi.write_cmd(Commands.GET_DEV_INFO)
    100         data = self.spi.read_data(20)
    101 

ImageBlock appears inappropriately resizes images

Class ImageBlock()

            if max(im.size) > min(self.padded_area):
                logging.debug(f'resizing image to fit area: {self.padded_area}')
#                 max_size = min(self.padded_area)
#                 resize = [max_size, max_size]
                im.thumbnail(self.padded_area, Image.BICUBIC)
                logging.debug(f'new image size: {im.size}')

Unclear what the issue is, but this does not appear to be working as expected and images are sometimes resized to a much smaller size..

Add better documentation for how TextBlocks choose font sizes and display overflow text

On Waveshare 6" (HD), I am using the default rotation of "0" (hence widescreen horizontal), all is good:

Horizontal-WS

If I change the rotation to 90 (vertical, cable up !caution, with the 6" WS HD!) this happens:

Vertical

my layout:

solar = {

'title' : {
        'type': 'TextBlock',
        'image': None,
        'inverse': False,
        'padding': 0,
        'width': .75,
        'height': .1,
        'hcenter': False,
        'vcenter': True,
        'abs_coordinates': (0, 0),
        # no need for calculations, just use x = 0 and y = 0
        'relative': False,
        'max_lines': 1,
        'font': dir_path+'/../../fonts/Montserrat/Montserrat-SemiBold.ttf',
        'mode': 'L'
     },
'time': {
        'type': 'TextBlock',
        'image': None,
        'padding': 0,
        'width': .25,
        'height': .1,
        'hcenter': False,
        'vcenter': True,
        'align': 'right',
        # calculate the X value and use 0 for the Y coordinate
        'abs_coordinates': (None, 0),
        # use the calculate the X coordinate based on the size of the 'title' block
        # use the absolute Y value from the 'time' block
        'relative': ['title', 'time'],
        'max_lines': 1,
        'mode': 'L',
        'font': dir_path+'/../../fonts/Montserrat/Montserrat-SemiBold.ttf',
    },
    'daily_yield': {
        'type': 'TextBlock',
        # need 2 lines to make this work properly; text is a bit too long
        # can you shorten the text?
        'max_lines': 2,
        'height': 0.22,
        'width': 1,
        'hcenter': False,
        'vcenter': True,
        'padding': 10,
        # use 0 for the X coordinate and calculate the Y coordinate
        'abs_coordinates': (0, None),
        # use the absolute S value from 'daily_yield and calculate the Y based on 'title'
        'relative': ['daily_yield', 'title'],
        'mode': 'L',
        'font':  dir_path+'/../../fonts/Montserrat/Montserrat-Regular.ttf'        
    },
   
    'current_consumption' : {
        'type': 'TextBlock',
        # need 2 lines to make this work properly; text is a bit too long
        # can you shorten the text?
        'max_lines': 2,
        'height': 0.22,
        'width': 1,
        'hcenter': False,
        'vcenter': True,
        'padding': 10,
        'abs_coordinates': (0, None),
        'relative': ['current_consumption', 'daily_yield'],
        'mode': 'L',
        'font':  dir_path+'/../../fonts/Montserrat/Montserrat-Regular.ttf'
    },
   
    'current_yield' : {
        'type': 'TextBlock',
        # need 2 lines to make this work properly; text is a bit too long
        # can you shorten the text?
        'max_lines': 2,
        'height': 0.22,
        'width': 1,
        'hcenter': False,
        'vcenter': True,
        'padding': 10,
        'abs_coordinates': (0, None),
        'relative': ['current_yield', 'current_consumption'],
        'mode': 'L',
        'font':  dir_path+'/../../fonts/Montserrat/Montserrat-Regular.ttf'
           
    },
   
   
    'battery_SOC' : {
        'type': 'TextBlock',
        # need 2 lines to make this work properly; text is a bit too long
        # can you shorten the text?
        'max_lines': 2,
        'height': 0.22,
        'width': 1,
        'hcenter': False,
        'vcenter': True,
        'padding': 10,
        'abs_coordinates': (0, None),
        'relative': ['battery_SOC', 'current_yield'],
        'mode': 'L',
        'font':  dir_path+'/../../fonts/Montserrat/Montserrat-Regular.ttf'
    }

}

Fiddling around with the height = 0.22 is working (taking it down to e.g. 0.12), but not intended as height of 0.22 is good for layout-style horizontal.
So is there a way to define different font sizing if using other rotation options than mentioned? Any help is highly appreciated!

Thank you
BR
Julius

Rotation of EPD display: Waveshare 6" (800x600), rotations dont match documentation

Hi, this is concerning the documented rotation options of EPD display:
With my waveshare 6" (800x600px - https://www.waveshare.com/wiki/6inch_e-Paper_HAT), the rotations dont match the documentation. Here this is matching:
cable left : 0; cable right: 180; cable top: 90; cable bottom: -90

rotation = 0:
Horizontal-WS

rotation = 90:

Vertical

If you need to run test-code against my setup, feel free to get in touch. If you need any more data, pictures, etc., also get in touch.

Thank you and BR
Julius

Update readme

TextBlock claim documents state that they are always rendered as 1 bit, this is not true. Text blocks support grayscale with supported displays.

waveshare colors do not match HTML colors

Orange and Green don't match. Need to add a check/convert for this

Name PILLOW RGB WaveShare HEX Waveshare RGB Variant
BLACK (0, 0, 0) 0X0 (0, 0, 0)
WHITE (255, 255, 255) 0X1 (255, 255, 255)
GREEN (0, 128, 0) 0X2 (0, 255, 0) โœ”๏ธ
BLUE (0, 0, 255) 0X3 (0, 0, 255)
RED (255, 0, 0) 0X4 (255, 0, 0)
YELLOW (255, 255, 0) 0X4 (255, 255, 0)
ORANGE (255, 165, 0) 0X5 (255, 128, 0) โœ”๏ธ

SPI File Handles remain open after refreshes causing eventual OSError [Error 24] and crash

03:37:41 epd2in7:getbuffer:378:DEBUG - Horizontal
Traceback (most recent call last):
  File "paperpi.py", line 658, in <module>
  File "paperpi.py", line 641, in main
  File "paperpi.py", line 545, in update_loop
  File "/home/pi/.local/share/virtualenvs/epd_display-ApAYs8Kw/lib/python3.7/site-packages/epdlib/Screen.py", line 361, in writeEPD
  File "/home/pi/.local/share/virtualenvs/epd_display-ApAYs8Kw/lib/python3.7/site-packages/epdlib/Screen.py", line 401, in _full_writeEPD_non_hd
  File "/home/pi/.local/share/virtualenvs/epd_display-ApAYs8Kw/lib/python3.7/site-packages/epdlib/Screen.py", line 296, in initEPD
  File "/home/pi/src/epd_display/paperpi/waveshare_epd/epd2in7.py", line 222, in init
  File "/home/pi/src/epd_display/paperpi/waveshare_epd/epdconfig.py", line 74, in module_init
OSError: [Errno 24] Too many open files
Traceback (most recent call last):
  File "/usr/lib/python3.7/weakref.py", line 628, in _exitfunc
  File "/usr/lib/python3.7/weakref.py", line 552, in __call__
  File "/usr/lib/python3.7/tempfile.py", line 934, in _cleanup
  File "/usr/lib/python3.7/shutil.py", line 487, in rmtree
  File "/usr/lib/python3.7/shutil.py", line 485, in rmtree
OSError: [Errno 24] Too many open files: '/tmp/PaperPi_489piixc'

This appears to be a problem in the WaveShare epdconfig.py. The SPI handle is opened, but it is not closed anywhere after writing.

Putting the display to sleep does not close out the SPI handle.

Adding an self.SPI.close() call to the module_exit() method does not appear to help

    def module_exit(self):
        logging.debug("spi end")
        self.SPI.close()


        logging.debug("close 5V, Module enters 0 power consumption ...")
        self.GPIO.output(self.RST_PIN, 0)
        self.GPIO.output(self.DC_PIN, 0)


        self.GPIO.cleanup()
        self.SPI.close() # this does not appear to help

crash when img is None in Screen.writeEPD

Traceback (most recent call last):
File "paperpi/paperpi.py", line 648, in
File "paperpi/paperpi.py", line 631, in main
File "paperpi/paperpi.py", line 469, in update_loop
File "epdlib/Screen.py", line 273, in wrapper
File "epdlib/Screen.py", line 535, in writeEPD
AttributeError: 'NoneType' object has no attribute 'rotate'

crashes when Nonetype is passed as image

Screen.py

line 253: self.buffer_no_image = self._epd.getbuffer(Image.new('L', self.resolution, 255))

line 291: self.buffer_no_image = self.epd.getbuffer(Image.new('L', self.resolution, 255))

line 382: image_buffer = epd.getbuffer(image) <--- this is likely the culprit of the crash

Dec  9 22:12:21 paperpi paperpi[949]: [950] Failed to execute script paperpi
Dec  9 22:12:21 paperpi paperpi[949]: Traceback (most recent call last):
Dec  9 22:12:21 paperpi paperpi[949]:   File "paperpi/paperpi.py", line 568, in <module>
Dec  9 22:12:21 paperpi paperpi[949]:   File "paperpi/paperpi.py", line 551, in main
Dec  9 22:12:21 paperpi paperpi[949]:   File "paperpi/paperpi.py", line 428, in update_loop
Dec  9 22:12:21 paperpi paperpi[949]:   File "epdlib/Screen.py", line 388, in writeEPD
Dec  9 22:12:21 paperpi paperpi[949]:   File "/tmp/_MEI3ExySM/waveshare_epd/epd7in5_V2.py", line 118, in getbuffer
Dec  9 22:12:21 paperpi paperpi[949]:     image_monocolor = image.convert('1')
Dec  9 22:12:21 paperpi paperpi[949]: AttributeError: 'NoneType' object has no attribute 'convert'
Dec  9 22:12:22 paperpi systemd[1]: paperpi-daemon.service: Main process exited, code=exited, status=255/EXCEPTION

Borders are not updated by `update_block_props`

Borders are not updated by update_block_props:
The .border_config property of the block should have a fill color of 'BLUE', but is stuck at 255:

{'fill': 255, 'width': 3, 'sides': ['top']}

This can likely be fixed from within the set_block method in Layout.Layout by checking for a fill value in the border_config property and updating the fill to match the fill value.

image

Make layouts more dynamic

Dynamic Layouts would provide:

  • color changes of fonts, backgrounds and drawing elements on each refresh
  • changes to block element sizes
  • font changes between refresh

Issues:

Layout.Layout objects are not designed to be updated once created. Updating values involves updating specific key/value combinations in the dictionary. Changes to block mode values may be easy to handle, but changes to the font or block size ratios will involve a recalculation and reset of many of the Block.Block object properties.

getsize deprecated in TextBlock class

The _calc_maxchar method in Block.TextBlock needs to be updated to use either getbox or getlength. One of these two methods should be more precise than the current bodge.

WARNING:root:no font size set, using {font_size}
/tmp/ipykernel_5447/3591940074.py:220: DeprecationWarning: getsize is deprecated and will be removed in Pillow 10 (2023-07-01). Use getbbox or getlength instead.
  s_length = self.font.getsize(s)[0] # string length in Pixles

cannot install inky/omni-epd in pipenv

@aaronr8684

I'm struggling to get omni-epd to install in my pipenv. Can you give any pointers for how you made this work? It keeps failing on numpy when it tries to install dependencies for inky. This is on a Buster32 with python 3.7.

I've tried everything I can think of, but I can't get numpy to install properly within the venv. It eventually always errors out with this:

      error: Command "arm-linux-gnueabihf-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-z,relro build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/umath/loops_unary_fp.dispatch.neon.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/umath/loops_arithmetic.dispatch.neon.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/umath/loops_unary_fp.dispatch.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/umath/loops_arithm_fp.dispatch.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/umath/loops_arithmetic.dispatch.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/umath/loops_trigonometric.dispatch.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/umath/loops_exponent_log.dispatch.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/umath/loops_trigonometric.dispatch.neon_vfpv4.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/abstractdtypes.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/alloc.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/arrayobject.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/multiarray/arraytypes.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/array_coercion.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/array_method.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/array_assign_scalar.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/array_assign_array.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/arrayfunction_override.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/buffer.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/calculation.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/compiled_base.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/common.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/common_dtype.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/convert.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/convert_datatype.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/conversion_utils.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/ctors.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/datetime.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/datetime_strings.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/datetime_busday.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/datetime_busdaycal.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/descriptor.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/dtypemeta.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/dragon4.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/dtype_transfer.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/multiarray/einsum.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/multiarray/einsum_sumprod.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/flagsobject.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/getset.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/hashdescr.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/item_selection.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/iterators.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/legacy_dtype_implementation.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/multiarray/lowlevel_strided_loops.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/mapping.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/methods.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/multiarraymodule.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/multiarray/nditer_templ.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/nditer_api.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/nditer_constr.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/nditer_pywrap.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/number.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/refcount.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/sequence.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/shape.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/scalarapi.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/multiarray/scalartypes.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/strfuncs.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/temp_elide.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/typeinfo.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/usertypes.o build/temp.linux-armv7l-3.7/numpy/core/src/multiarray/vdot.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/npysort/quicksort.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/npysort/mergesort.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/npysort/timsort.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/npysort/heapsort.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/npysort/radixsort.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/npysort/selection.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/npysort/binsearch.o build/temp.linux-armv7l-3.7/numpy/core/src/umath/umathmodule.o build/temp.linux-armv7l-3.7/numpy/core/src/umath/reduction.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/umath/loops.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/umath/matmul.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/umath/clip.o build/temp.linux-armv7l-3.7/numpy/core/src/umath/ufunc_object.o build/temp.linux-armv7l-3.7/numpy/core/src/umath/extobj.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/umath/scalarmath.o build/temp.linux-armv7l-3.7/numpy/core/src/umath/ufunc_type_resolution.o build/temp.linux-armv7l-3.7/numpy/core/src/umath/override.o build/temp.linux-armv7l-3.7/numpy/core/src/common/array_assign.o build/temp.linux-armv7l-3.7/numpy/core/src/common/mem_overlap.o build/temp.linux-armv7l-3.7/numpy/core/src/common/npy_argparse.o build/temp.linux-armv7l-3.7/numpy/core/src/common/npy_longdouble.o build/temp.linux-armv7l-3.7/numpy/core/src/common/ucsnarrow.o build/temp.linux-armv7l-3.7/numpy/core/src/common/ufunc_override.o build/temp.linux-armv7l-3.7/numpy/core/src/common/numpyos.o build/temp.linux-armv7l-3.7/build/src.linux-armv7l-3.7/numpy/core/src/common/npy_cpu_features.o build/temp.linux-armv7l-3.7/numpy/core/src/common/cblasfuncs.o build/temp.linux-armv7l-3.7/numpy/core/src/common/python_xerbla.o -L/usr/lib/arm-linux-gnueabihf -L/usr/lib -Lbuild/temp.linux-armv7l-3.7 -lnpymath -lf77blas -lcblas -latlas -lf77blas -lcblas -lm -o build/lib.linux-armv7l-3.7/numpy/core/_multiarray_umath.cpython-37m-arm-linux-gnueabihf.so" failed with exit status 1
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for numpy
ERROR: Could not build wheels for numpy, which is required to install pyproject.toml-based projects

import omni_epd

italic fonts sometimes cut off at left edge of TextBlocks

Lower case descenders on some italic fonts stretch outside of the measured bounding box as calculated by Pillow. Adjusting the anchors for measurement bounds of multiline_textbox. This causes the left hand pixels to be cut off in some instances. the letters p, g, j appear to be most affected.

TextBlock currently uses the anchor ld, which should cover this, but for some reason some italic fonts are miss calcuated.
https://pillow.readthedocs.io/en/stable/handbook/text-anchors.html#text-anchors

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.