// SPDX-License-Identifier: GPL-2.0-or-later
/* pps-css-utils.h
 * this file is part of papers, a gnome document viewer
 *
 * Copyright (C) Christian Hegert (copied from libdazzle)
 */

#include "pps-css-utils.h"
#include <math.h>

/* copy/paste from libdazzle */

#define FONT_FAMILY "font-family"
#define FONT_STYLE "font-style"
#define FONT_VARIANT "font-variant"
#define FONT_STRETCH "font-stretch"
#define FONT_WEIGHT "font-weight"
#define FONT_SIZE "font-size"

/**
 * SECTION:dzl-pango
 * @title: Pango Utilities
 * @short_description: Utilities to use with the Pango text layout library
 */

/**
 * dzl_pango_font_description_to_css:
 *
 * This function will generate CSS suitable for Gtk's CSS engine
 * based on the properties of the #PangoFontDescription.
 *
 * Returns: (transfer full): A newly allocated string containing the
 *    CSS describing the font description.
 */
gchar *
dzl_pango_font_description_to_css (const PangoFontDescription *font_desc)
{
	PangoFontMask mask;
	GString *str;

#define ADD_KEYVAL(key, fmt) g_string_append (str, key ":" fmt ";")
#define ADD_KEYVAL_PRINTF(key, fmt, ...) g_string_append_printf (str, key ":" fmt ";", __VA_ARGS__)

	g_return_val_if_fail (font_desc, NULL);

	str = g_string_new (NULL);

	mask = pango_font_description_get_set_fields (font_desc);

	if ((mask & PANGO_FONT_MASK_FAMILY) != 0) {
		const gchar *family;

		family = pango_font_description_get_family (font_desc);
		ADD_KEYVAL_PRINTF (FONT_FAMILY, "\"%s\"", family);
	}

	if ((mask & PANGO_FONT_MASK_STYLE) != 0) {
		PangoStyle style;

		style = pango_font_description_get_style (font_desc);

		switch (style) {
		case PANGO_STYLE_NORMAL:
			ADD_KEYVAL (FONT_STYLE, "normal");
			break;

		case PANGO_STYLE_OBLIQUE:
			ADD_KEYVAL (FONT_STYLE, "oblique");
			break;

		case PANGO_STYLE_ITALIC:
			ADD_KEYVAL (FONT_STYLE, "italic");
			break;

		default:
			break;
		}
	}

	if ((mask & PANGO_FONT_MASK_VARIANT) != 0) {
		PangoVariant variant;

		variant = pango_font_description_get_variant (font_desc);

		switch (variant) {
		case PANGO_VARIANT_NORMAL:
			ADD_KEYVAL (FONT_VARIANT, "normal");
			break;

		case PANGO_VARIANT_SMALL_CAPS:
			ADD_KEYVAL (FONT_VARIANT, "small-caps");
			break;

		default:
			break;
		}
	}

	if ((mask & PANGO_FONT_MASK_WEIGHT)) {
		gint weight;

		weight = pango_font_description_get_weight (font_desc);

		/*
		 * WORKAROUND:
		 *
		 * font-weight with numbers does not appear to be working as expected
		 * right now. So for the common (bold/normal), let's just use the string
		 * and let gtk warn for the other values, which shouldn't really be
		 * used for this.
		 */

		switch (weight) {
		case PANGO_WEIGHT_SEMILIGHT:
			/*
			 * 350 is not actually a valid css font-weight, so we will just round
			 * up to 400.
			 */
		case PANGO_WEIGHT_NORMAL:
			ADD_KEYVAL (FONT_WEIGHT, "normal");
			break;

		case PANGO_WEIGHT_BOLD:
			ADD_KEYVAL (FONT_WEIGHT, "bold");
			break;

		case PANGO_WEIGHT_THIN:
		case PANGO_WEIGHT_ULTRALIGHT:
		case PANGO_WEIGHT_LIGHT:
		case PANGO_WEIGHT_BOOK:
		case PANGO_WEIGHT_MEDIUM:
		case PANGO_WEIGHT_SEMIBOLD:
		case PANGO_WEIGHT_ULTRABOLD:
		case PANGO_WEIGHT_HEAVY:
		case PANGO_WEIGHT_ULTRAHEAVY:
		default:
			/* round to nearest hundred */
			weight = round (weight / 100.0) * 100;
			ADD_KEYVAL_PRINTF ("font-weight", "%d", weight);
			break;
		}
	}

#ifndef GDK_WINDOWING_QUARTZ
	/*
	 * We seem to get "Condensed" for fonts on the Quartz backend,
	 * which is rather annoying as it results in us always hitting
	 * fallback (stretch) paths. So let's cheat and just disable
	 * stretch support for now on Quartz.
	 */
	if ((mask & PANGO_FONT_MASK_STRETCH)) {
		switch (pango_font_description_get_stretch (font_desc)) {
		case PANGO_STRETCH_ULTRA_CONDENSED:
			ADD_KEYVAL (FONT_STRETCH, "ultra-condensed");
			break;

		case PANGO_STRETCH_EXTRA_CONDENSED:
			ADD_KEYVAL (FONT_STRETCH, "extra-condensed");
			break;

		case PANGO_STRETCH_CONDENSED:
			ADD_KEYVAL (FONT_STRETCH, "condensed");
			break;

		case PANGO_STRETCH_SEMI_CONDENSED:
			ADD_KEYVAL (FONT_STRETCH, "semi-condensed");
			break;

		case PANGO_STRETCH_NORMAL:
			ADD_KEYVAL (FONT_STRETCH, "normal");
			break;

		case PANGO_STRETCH_SEMI_EXPANDED:
			ADD_KEYVAL (FONT_STRETCH, "semi-expanded");
			break;

		case PANGO_STRETCH_EXPANDED:
			ADD_KEYVAL (FONT_STRETCH, "expanded");
			break;

		case PANGO_STRETCH_EXTRA_EXPANDED:
			ADD_KEYVAL (FONT_STRETCH, "extra-expanded");
			break;

		case PANGO_STRETCH_ULTRA_EXPANDED:
			ADD_KEYVAL (FONT_STRETCH, "ultra-expanded");
			break;

		default:
			break;
		}
	}
#endif

	return g_string_free (str, FALSE);

#undef ADD_KEYVAL
#undef ADD_KEYVAL_PRINTF
}

/**
 * pps_css_utils_filter_class_chars:
 * @str: input string
 *
 * Returns @str as a new string removing any character which
 * is not allowed in a css class name.
 *
 * Css class names should only contain following characters:
 *   - a–z A–Z 0–9 - _
 *   - any Unicode character ≥ U+00A0
 *
 * Returns: (transfer full) (not nullable): newly allocated string
 */
char *
pps_css_utils_filter_class_chars (const char *str)
{
	const char *p;
	gboolean keep;
	gunichar ch;

	if (!str)
		return g_strdup ("(null)");

	GString *result = g_string_sized_new (strlen (str));

	for (p = str; *p; p = g_utf8_next_char (p)) {
		ch = g_utf8_get_char (p);
		keep = FALSE;

		if (ch <= 0x7F) { /* ASCII fast path */
			if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
			    (ch >= '0' && ch <= '9') || ch == '-' || ch == '_') {
				keep = TRUE;
			}
		} else if (ch >= 0x00A0) { /* Unicode ≥ U+00A0 → keep */
			keep = TRUE;
		}

		if (keep)
			g_string_append_unichar (result, ch);
	}
	return g_string_free (result, FALSE);
}
