Skip to content

Commit

Permalink
Render text using Pango framework
Browse files Browse the repository at this point in the history
Adds options to configure font face, size, and color for text output.

Signed-off-by: Artem Senichev <[email protected]>
  • Loading branch information
artemsen committed Jun 23, 2022
1 parent b65a099 commit 3f2f73a
Show file tree
Hide file tree
Showing 14 changed files with 315 additions and 195 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ on: [push]
jobs:
build:

runs-on: ubuntu-20.04
runs-on: ubuntu-22.04

steps:
- name: Install dependencies
run: >
sudo apt install --no-install-recommends --yes
build-essential meson pkg-config wayland-protocols
libwayland-dev libcairo-dev libjson-c-dev libxkbcommon-dev
libgif-dev libjpeg-dev librsvg2-dev libwebp-dev
libpango1.0-dev libpangocairo-1.0-0
libavif-dev libgif-dev libjpeg-dev librsvg2-dev libwebp-dev
- name: Check out source code
uses: actions/[email protected]
Expand Down
6 changes: 6 additions & 0 deletions extra/swayimgrc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ background = grid
# Show image info: format, size, EXIF, and current scale (yes/no)
info = no

# Font used to show image info
font = monospace 10

# Text color
color = b4b4b4

# Window class/app_id (default is autogenerated swayimg_XXXX)
#app_id = swayimg

Expand Down
3 changes: 3 additions & 0 deletions extra/swayimgrc.5
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Empty lines and comments are ignored.
.IP "\fBfullscreen\fR: start in full screen mode, possible values are \fIyes\fR or \fIno\fR;"
.IP "\fBbackground\fR: background mode or color, possible values are \fIgrid\fR or RGB hex color (e.g. \fI1a2b3c\fR);"
.IP "\fBinfo\fR: show image meta information: format, size, EXIF, and current scale, possible values are \fIyes\fR or \fIno\fR;"
.IP "\fBfont\fR: font name and size used to render image info text in pango format;"
.IP "\fBcolor\fR: color used to render text, RGB hex format (e.g. \fI1a2b3c\fR);"
.IP "\fBapp_id\fR: set a constant window class/app_id, setting this may break the window layout;"
.IP "\fBrules\fR: enable or disable adding Sway rules (window position management), possible values are \fIyes\fR or \fIno\fR."
.\" example file
Expand All @@ -42,6 +44,7 @@ scale = fit
fullscreen = no
background = 1a2b3c
info = yes
font = monospace 10
.EE
.\" related man pages
.SH SEE ALSO
Expand Down
27 changes: 16 additions & 11 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,21 @@ version = get_option('version')
# mandatory dependencies
wlcln = dependency('wayland-client')
cairo = dependency('cairo')
json = dependency('json-c')
xkb = dependency('xkbcommon')
rt = cc.find_library('rt')
json = dependency('json-c')
xkb = dependency('xkbcommon')
pango = dependency('pango')
pangocairo = dependency('pangocairo')
rt = cc.find_library('rt')
# optional dependencies: file formats support
avif = dependency('libavif', required: get_option('avif'))
gif = cc.find_library('gif', required: get_option('gif'))
jpeg = dependency('libjpeg', required: get_option('jpeg'))
png = dependency('libpng', required: get_option('png'))
rsvg = dependency('librsvg-2.0', version: '>=2.46', required: get_option('svg'))
webp = dependency('libwebp', required: get_option('webp'))
avif = dependency('libavif', required: get_option('avif'))
gif = cc.find_library('gif', required: get_option('gif'))
jpeg = dependency('libjpeg', required: get_option('jpeg'))
png = dependency('libpng', required: get_option('png'))
rsvg = dependency('librsvg-2.0', version: '>=2.46', required: get_option('svg'))
webp = dependency('libwebp', required: get_option('webp'))
# optional dependencies: other features
exif = dependency('libexif', required: get_option('exif'))
bash = dependency('bash-completion', required: get_option('bash'))
exif = dependency('libexif', required: get_option('exif'))
bash = dependency('bash-completion', required: get_option('bash'))
# Arch specific: https://bugs.archlinux.org/task/73931
jxl_feature = get_option('jxl')
jxl = dependency('libjxl', required: false)
Expand Down Expand Up @@ -117,6 +119,7 @@ sources = [
'src/image.c',
'src/main.c',
'src/sway.c',
'src/text.c',
'src/viewer.c',
'src/window.c',
'src/formats/bmp.c',
Expand Down Expand Up @@ -160,6 +163,8 @@ executable(
jpeg,
json,
jxl,
pango,
pangocairo,
png,
rsvg,
rt,
Expand Down
40 changes: 0 additions & 40 deletions src/canvas.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@
#include <stdlib.h>
#include <string.h>

// Text render parameters
#define FONT_FAMILY "monospace"
#define FONT_SIZE 16
#define LINE_SPACING 2
#define TEXT_COLOR 0xb2b2b2
#define TEXT_SHADOW 0x101010

// Background grid parameters
#define GRID_STEP 10
#define GRID_COLOR1 0x333333
Expand Down Expand Up @@ -146,39 +139,6 @@ void draw_grid(const canvas_t* canvas, cairo_surface_t* image, cairo_t* cairo)
cairo_surface_mark_dirty(wnd_surface);
}

void draw_text(cairo_t* cairo, int x, int y, const char* text)
{
// set font
cairo_font_face_t* font = cairo_toy_font_face_create(
FONT_FAMILY, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_face(cairo, font);
cairo_set_font_size(cairo, FONT_SIZE);

// shadow
cairo_set_source_rgb(cairo, RGB_RED(TEXT_SHADOW), RGB_GREEN(TEXT_SHADOW),
RGB_BLUE(TEXT_SHADOW));
cairo_move_to(cairo, x + 1, y + 1 + FONT_SIZE);
cairo_show_text(cairo, text);

// normal text
cairo_set_source_rgb(cairo, RGB_RED(TEXT_COLOR), RGB_GREEN(TEXT_COLOR),
RGB_BLUE(TEXT_COLOR));
cairo_move_to(cairo, x, y + FONT_SIZE);
cairo_show_text(cairo, text);

cairo_set_font_face(cairo, NULL);
cairo_font_face_destroy(font);
}

void draw_lines(cairo_t* cairo, int x, int y, const char** lines)
{
while (*lines) {
draw_text(cairo, x, y, *lines);
++lines;
y += FONT_SIZE + LINE_SPACING;
}
}

bool move_viewpoint(canvas_t* canvas, cairo_surface_t* image, move_t direction)
{
const int img_w = canvas->scale * cairo_image_surface_get_width(image);
Expand Down
18 changes: 0 additions & 18 deletions src/canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,24 +88,6 @@ void draw_image(const canvas_t* canvas, cairo_surface_t* image, cairo_t* cairo);
*/
void draw_grid(const canvas_t* canvas, cairo_surface_t* image, cairo_t* cairo);

/**
* Draw text line.
* @param[in] cairo paint context
* @param[in] x left offset
* @param[in] y top offset
* @param[in] text text to draw
*/
void draw_text(cairo_t* cairo, int x, int y, const char* text);

/**
* Draw multiline text.
* @param[in] cairo paint context
* @param[in] x left offset
* @param[in] y top offset
* @param[in] lines array of strings, last line must be NULL
*/
void draw_lines(cairo_t* cairo, int x, int y, const char** lines);

/**
* Move view point.
* @param[in] canvas canvas context
Expand Down
92 changes: 64 additions & 28 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
/** Max length of window class/app_id name */
#define APP_ID_MAX 32

/** Default font name/size */
#define FONT_FACE "monospace 10"
/** Default text color. */
#define TEXT_COLOR 0xb4b4b4

/** Config file location. */
typedef struct {
const char* prefix; ///< Environment variable name
Expand Down Expand Up @@ -86,6 +91,44 @@ static bool set_boolean(const char* text, bool* value)
return rc;
}

/**
* Convert text value to RGV color.
* @param[in] text text to convert
* @param[out] value target variable
* @return false if value has invalid format
*/
static bool set_color(const char* text, uint32_t* value)
{
const unsigned long rgb = strtoul(text, NULL, 16);
if (rgb > 0x00ffffff || errno == ERANGE || (rgb == 0 && errno == EINVAL)) {
return false;
}
*value = rgb;
return true;
}

/**
* Set (replace) string in config parameter.
* @param[in] src source string
* @param[out] dst destination buffer
* @return false if not enough memory
*/
static bool set_string(const char* src, char** dst)
{
const size_t len = strlen(src) + 1 /*last null*/;
char* ptr = malloc(len);
if (!ptr) {
fprintf(stderr, "Not enough memory\n");
return false;
}
memcpy(ptr, src, len);

free(*dst);
*dst = ptr;

return true;
}

/**
* Apply property to configuration.
* @param[out] cfg target configuration instance
Expand All @@ -104,6 +147,10 @@ static bool apply_conf(config_t* cfg, const char* key, const char* value)
return set_background_config(cfg, value);
} else if (strcmp(key, "info") == 0) {
return set_boolean(value, &cfg->show_info);
} else if (strcmp(key, "font") == 0) {
return set_font_config(cfg, value);
} else if (strcmp(key, "color") == 0) {
return set_color(value, &cfg->font_color);
} else if (strcmp(key, "app_id") == 0) {
return set_appid_config(cfg, value);
} else if (strcmp(key, "rules") == 0) {
Expand All @@ -130,6 +177,8 @@ static config_t* default_config(void)
cfg->scale = scale_fit_or100;
cfg->background = BACKGROUND_GRID;
cfg->sway_rules = true;
set_font_config(cfg, FONT_FACE);
cfg->font_color = TEXT_COLOR;

// create unique application id
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
Expand Down Expand Up @@ -228,6 +277,7 @@ config_t* init_config(void)
void free_config(config_t* cfg)
{
if (cfg) {
free((void*)cfg->font_face);
free((void*)cfg->app_id);
free(cfg);
}
Expand Down Expand Up @@ -273,21 +323,13 @@ bool set_scale_config(config_t* cfg, const char* scale)

bool set_background_config(config_t* cfg, const char* background)
{
uint32_t bkg;

if (strcmp(background, "grid") == 0) {
bkg = BACKGROUND_GRID;
} else {
bkg = strtoul(background, NULL, 16);
if (bkg > 0x00ffffff || errno == ERANGE ||
(bkg == 0 && errno == EINVAL)) {
fprintf(stderr, "Invalid background: %s\n", background);
fprintf(stderr, "Expected 'grid' or RGB hex value.\n");
return false;
}
cfg->background = BACKGROUND_GRID;
} else if (!set_color(background, &cfg->background)) {
fprintf(stderr, "Invalid background: %s\n", background);
fprintf(stderr, "Expected 'grid' or RGB hex value.\n");
return false;
}

cfg->background = bkg;
return true;
}

Expand Down Expand Up @@ -325,28 +367,22 @@ bool set_geometry_config(config_t* cfg, const char* geometry)

bool set_appid_config(config_t* cfg, const char* app_id)
{
char* ptr;
size_t len;

len = app_id ? strlen(app_id) : 0;
const size_t len = app_id ? strlen(app_id) : 0;
if (len == 0 || len > APP_ID_MAX) {
fprintf(stderr, "Invalid class/app_id: %s\n", app_id);
fprintf(stderr, "Expected non-empty string up to %d chars.\n",
APP_ID_MAX);
return false;
}
return set_string(app_id, (char**)&cfg->app_id);
}

++len; // add last null

ptr = malloc(len);
if (!ptr) {
fprintf(stderr, "Not enough memory\n");
bool set_font_config(config_t* cfg, const char* font)
{
const size_t len = font ? strlen(font) : 0;
if (len == 0) {
fprintf(stderr, "Invalid font description: %s\n", font);
return false;
}
memcpy(ptr, app_id, len);

free((void*)cfg->app_id);
cfg->app_id = ptr;

return true;
return set_string(font, (char**)&cfg->font_face);
}
24 changes: 17 additions & 7 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@

/** App configuration. */
typedef struct {
scale_t scale; ///< Initial scale
rect_t window; ///< Window geometry
uint32_t background; ///< Background mode/color
bool fullscreen; ///< Full screen mode
bool show_info; ///< Show image info
const char* app_id; ///< Window class/app_id name
bool sway_rules; ///< Enable/disable Sway rules
scale_t scale; ///< Initial scale
rect_t window; ///< Window geometry
uint32_t background; ///< Background mode/color
bool fullscreen; ///< Full screen mode
bool show_info; ///< Show image info
const char* font_face; ///< Font name and size (pango format)
uint32_t font_color; ///< Font color
const char* app_id; ///< Window class/app_id name
bool sway_rules; ///< Enable/disable Sway rules
} config_t;

/**
Expand Down Expand Up @@ -66,3 +68,11 @@ bool set_geometry_config(config_t* cfg, const char* geometry);
* @return false if value format is invalid
*/
bool set_appid_config(config_t* cfg, const char* app_id);

/**
* Set font name and size.
* @param[out] cfg target configuration instance
* @param[in] font font name and size in pango format
* @return false if value format is invalid
*/
bool set_font_config(config_t* cfg, const char* font);
4 changes: 2 additions & 2 deletions src/exif.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ static void add_meta(image_t* img, ExifData* exif, ExifTag tag,
if (entry) {
exif_entry_get_value(entry, value, sizeof(value));
if (*value) {
add_image_meta(img, name, value);
add_image_info(img, name, value);
}
}
}
Expand Down Expand Up @@ -133,7 +133,7 @@ static void read_location(image_t* img, ExifData* exif)
if (read_coordinate(exif, EXIF_TAG_GPS_LONGITUDE,
EXIF_TAG_GPS_LONGITUDE_REF, location + pos,
sizeof(location) - pos)) {
add_image_meta(img, "Location", location);
add_image_info(img, "Location", location);
}
}
}
Expand Down
Loading

0 comments on commit 3f2f73a

Please sign in to comment.