- add an owner info to PS.
- support "document-charset" attribute.
This commit is contained in:
parent
b0d6f2d66b
commit
d0e073a2d3
@ -1,14 +1,18 @@
|
||||
diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .deps -x .libs -x '*.ps' -x '*a' -x '*~' -x '*o' -x 'stamp*' -x paps -x test_libpaps paps-0.6.6.orig/src/libpaps.c paps-0.6.6/src/libpaps.c
|
||||
diff -ruN paps-0.6.6.orig/src/libpaps.c paps-0.6.6/src/libpaps.c
|
||||
--- paps-0.6.6.orig/src/libpaps.c 2006-04-27 04:09:03.000000000 +0900
|
||||
+++ paps-0.6.6/src/libpaps.c 2006-07-03 21:37:01.000000000 +0900
|
||||
@@ -56,12 +56,16 @@
|
||||
+++ paps-0.6.6/src/libpaps.c 2006-07-17 13:33:13.000000000 +0900
|
||||
@@ -52,16 +52,20 @@
|
||||
int last_char_idx;
|
||||
double last_pos_y;
|
||||
double last_pos_x;
|
||||
+ double scale_x;
|
||||
+ double scale_y;
|
||||
} paps_private_t;
|
||||
|
||||
|
||||
// Forward declarations
|
||||
-static void add_postscript_prologue(GString *ps_string);
|
||||
+static void add_postscript_prologue(GString *ps_string,
|
||||
+ gdouble scale_x,
|
||||
+ gdouble scale_y);
|
||||
+static void add_postscript_prologue(paps_private_t *paps);
|
||||
static gchar *get_next_char_id_strdup(paps_private_t *paps);
|
||||
static void add_line_to_postscript(paps_private_t *paps,
|
||||
GString *line_str,
|
||||
@ -19,12 +23,15 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
PangoLayoutLine *line);
|
||||
|
||||
paps_t *paps_new()
|
||||
@@ -77,11 +81,21 @@
|
||||
@@ -76,12 +80,27 @@
|
||||
paps->last_pos_x = -1e67;
|
||||
paps->last_pos_y = -1e67;
|
||||
paps->last_char_idx = 0;
|
||||
+ paps->scale_x = 1.0;
|
||||
+ paps->scale_y = 1.0;
|
||||
|
||||
- add_postscript_prologue(paps->header);
|
||||
+ add_postscript_prologue(paps->header, 1.0, 1.0);
|
||||
+ add_postscript_prologue(paps);
|
||||
|
||||
return paps;
|
||||
}
|
||||
@ -35,14 +42,17 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
+ gdouble scale_y)
|
||||
+{
|
||||
+ paps_private_t *paps = (paps_private_t *)paps_;
|
||||
+
|
||||
+ paps->scale_x = scale_x;
|
||||
+ paps->scale_y = scale_y;
|
||||
+ g_string_erase(paps->header, 0, -1);
|
||||
+ add_postscript_prologue(paps->header, scale_x, scale_y);
|
||||
+ add_postscript_prologue(paps);
|
||||
+}
|
||||
+
|
||||
PangoContext *paps_get_pango_context()
|
||||
{
|
||||
return pango_ft2_get_context (PAPS_DPI, PAPS_DPI);
|
||||
@@ -124,14 +138,18 @@
|
||||
@@ -124,14 +143,18 @@
|
||||
GString *line_str,
|
||||
PangoLayoutLine *pango_line,
|
||||
double line_start_pos_x,
|
||||
@ -63,7 +73,7 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
);
|
||||
/* Countour traveling functions */
|
||||
static int paps_ps_move_to( FT_Vector* to,
|
||||
@@ -166,6 +184,8 @@
|
||||
@@ -166,6 +189,8 @@
|
||||
gchar *paps_layout_to_postscript_strdup(paps_t *paps_,
|
||||
double pos_x,
|
||||
double pos_y,
|
||||
@ -72,7 +82,7 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
PangoLayout *layout)
|
||||
{
|
||||
paps_private_t *paps = (paps_private_t*)paps_;
|
||||
@@ -189,6 +209,8 @@
|
||||
@@ -189,6 +214,8 @@
|
||||
layout_str,
|
||||
pos_x,
|
||||
pos_y,
|
||||
@ -81,7 +91,7 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
pango_line);
|
||||
|
||||
pos_y -= logical_rect.height * scale;
|
||||
@@ -203,6 +225,8 @@
|
||||
@@ -203,6 +230,8 @@
|
||||
gchar *paps_layout_line_to_postscript_strdup(paps_t *paps_,
|
||||
double pos_x,
|
||||
double pos_y,
|
||||
@ -90,7 +100,7 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
PangoLayoutLine *layout_line)
|
||||
{
|
||||
paps_private_t *paps = (paps_private_t*)paps_;
|
||||
@@ -213,6 +237,8 @@
|
||||
@@ -213,6 +242,8 @@
|
||||
layout_str,
|
||||
pos_x,
|
||||
pos_y,
|
||||
@ -99,18 +109,29 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
layout_line);
|
||||
|
||||
ret_str = layout_str->str;
|
||||
@@ -221,7 +247,9 @@
|
||||
@@ -221,9 +252,10 @@
|
||||
return ret_str;
|
||||
}
|
||||
|
||||
-void add_postscript_prologue(GString *ps_string)
|
||||
+void add_postscript_prologue(GString *ps_string,
|
||||
+ gdouble scale_x,
|
||||
+ gdouble scale_y)
|
||||
+static void
|
||||
+add_postscript_prologue(paps_private_t *paps)
|
||||
{
|
||||
g_string_append_printf(ps_string,
|
||||
- g_string_append_printf(ps_string,
|
||||
+ g_string_append_printf(paps->header,
|
||||
"%%%%BeginProlog\n"
|
||||
@@ -249,7 +277,7 @@
|
||||
"/papsdict 1 dict def\n"
|
||||
"papsdict begin\n"
|
||||
@@ -231,7 +263,7 @@
|
||||
);
|
||||
|
||||
/* Outline support */
|
||||
- g_string_append_printf(ps_string,
|
||||
+ g_string_append_printf(paps->header,
|
||||
"/conicto {\n"
|
||||
" /to_y exch def\n"
|
||||
" /to_x exch def\n"
|
||||
@@ -249,7 +281,7 @@
|
||||
"/start_ol { gsave } bind def\n"
|
||||
"/end_ol { closepath fill grestore } bind def\n"
|
||||
/* Specify both x and y. */
|
||||
@ -119,17 +140,34 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
"/goto_xy { fontdict begin /last_y exch def /last_x exch def end } def\n"
|
||||
"/goto_x { fontdict begin /last_x exch def end } def\n"
|
||||
"/fwd_x { fontdict begin /last_x exch last_x add def end } def\n"
|
||||
@@ -261,7 +289,8 @@
|
||||
@@ -261,14 +293,15 @@
|
||||
// The scaling is a combination of the scaling due
|
||||
// to the dpi and the difference in the coordinate
|
||||
// systems of postscript and freetype2.
|
||||
- 1.0 / PAPS_DPI
|
||||
+ 1.0 / PAPS_DPI,
|
||||
+ scale_x, scale_y
|
||||
+ paps->scale_x, paps->scale_y
|
||||
);
|
||||
|
||||
// The following is a dispatcher for an encoded string that contains
|
||||
@@ -331,6 +360,8 @@
|
||||
// a packed version of the pango layout data. Currently it just executes
|
||||
// the symbols corresponding to the encoded characters, but in the future
|
||||
// it will also contain some meta data, e.g. the size of the layout.
|
||||
- g_string_append_printf(ps_string,
|
||||
+ g_string_append_printf(paps->header,
|
||||
"/paps_exec {\n"
|
||||
" 1 dict begin\n"
|
||||
" /ps exch def\n"
|
||||
@@ -320,7 +353,7 @@
|
||||
);
|
||||
|
||||
/* Open up dictionaries */
|
||||
- g_string_append(ps_string,
|
||||
+ g_string_append(paps->header,
|
||||
"/fontdict 1 dict def\n"
|
||||
"papsdict begin fontdict begin\n");
|
||||
}
|
||||
@@ -331,6 +364,8 @@
|
||||
GString *line_str,
|
||||
double x_pos,
|
||||
double y_pos,
|
||||
@ -138,7 +176,7 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
PangoLayoutLine *line)
|
||||
{
|
||||
PangoRectangle ink_rect, logical_rect;
|
||||
@@ -349,7 +380,7 @@
|
||||
@@ -349,7 +384,7 @@
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -147,7 +185,7 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
}
|
||||
|
||||
/* draw_contour() draws all of the contours that make up a line.
|
||||
@@ -359,7 +390,9 @@
|
||||
@@ -359,7 +394,9 @@
|
||||
GString *layout_str,
|
||||
PangoLayoutLine *pango_line,
|
||||
double line_start_pos_x,
|
||||
@ -158,7 +196,7 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
)
|
||||
{
|
||||
GSList *runs_list;
|
||||
@@ -389,7 +422,7 @@
|
||||
@@ -389,7 +426,7 @@
|
||||
glyph_pos_x = x_pos + 1.0*geometry.x_offset * scale;
|
||||
glyph_pos_y = line_start_pos_y - 1.0*geometry.y_offset * scale;
|
||||
|
||||
@ -167,7 +205,7 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
|
||||
if (glyphs->glyphs[glyph_idx].glyph == PANGO_GLYPH_EMPTY)
|
||||
continue;
|
||||
@@ -399,7 +432,9 @@
|
||||
@@ -399,7 +436,9 @@
|
||||
ft_face,
|
||||
&glyphs->glyphs[glyph_idx],
|
||||
glyph_pos_x,
|
||||
@ -178,7 +216,7 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
);
|
||||
}
|
||||
|
||||
@@ -415,13 +450,15 @@
|
||||
@@ -415,13 +454,15 @@
|
||||
FT_Face face,
|
||||
PangoGlyphInfo *glyph_info,
|
||||
double pos_x,
|
||||
@ -196,7 +234,7 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
gchar *id = NULL;
|
||||
|
||||
/* Output outline */
|
||||
@@ -480,7 +517,7 @@
|
||||
@@ -480,7 +521,7 @@
|
||||
g_string_append_printf(glyph_def_string,
|
||||
"%.0f fwd_x\n"
|
||||
"end_ol\n",
|
||||
@ -205,9 +243,9 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
);
|
||||
|
||||
// TBD - Check if the glyph_def_string is empty. If so, set the
|
||||
diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .deps -x .libs -x '*.ps' -x '*a' -x '*~' -x '*o' -x 'stamp*' -x paps -x test_libpaps paps-0.6.6.orig/src/libpaps.h paps-0.6.6/src/libpaps.h
|
||||
diff -ruN paps-0.6.6.orig/src/libpaps.h paps-0.6.6/src/libpaps.h
|
||||
--- paps-0.6.6.orig/src/libpaps.h 2005-12-21 04:35:39.000000000 +0900
|
||||
+++ paps-0.6.6/src/libpaps.h 2006-07-03 20:17:13.000000000 +0900
|
||||
+++ paps-0.6.6/src/libpaps.h 2006-07-17 13:27:59.000000000 +0900
|
||||
@@ -40,6 +40,19 @@
|
||||
*/
|
||||
void paps_free(paps_t *paps);
|
||||
@ -246,9 +284,9 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
PangoLayoutLine *layout_line);
|
||||
|
||||
/**
|
||||
diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .deps -x .libs -x '*.ps' -x '*a' -x '*~' -x '*o' -x 'stamp*' -x paps -x test_libpaps paps-0.6.6.orig/src/paps.c paps-0.6.6/src/paps.c
|
||||
--- paps-0.6.6.orig/src/paps.c 2006-07-04 12:25:33.000000000 +0900
|
||||
+++ paps-0.6.6/src/paps.c 2006-07-04 13:11:32.000000000 +0900
|
||||
diff -ruN paps-0.6.6.orig/src/paps.c paps-0.6.6/src/paps.c
|
||||
--- paps-0.6.6.orig/src/paps.c 2006-07-17 13:27:27.000000000 +0900
|
||||
+++ paps-0.6.6/src/paps.c 2006-07-17 13:27:59.000000000 +0900
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
|
||||
@ -585,9 +623,9 @@ diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .de
|
||||
return logical_rect.height;
|
||||
}
|
||||
-
|
||||
diff -ruN -x 'Makefile*' -x autom4te.cache -x 'config*' -x doc -x libtool -x .deps -x .libs -x '*.ps' -x '*a' -x '*~' -x '*o' -x 'stamp*' -x paps -x test_libpaps paps-0.6.6.orig/src/test_libpaps.c paps-0.6.6/src/test_libpaps.c
|
||||
diff -ruN paps-0.6.6.orig/src/test_libpaps.c paps-0.6.6/src/test_libpaps.c
|
||||
--- paps-0.6.6.orig/src/test_libpaps.c 2005-12-21 04:35:39.000000000 +0900
|
||||
+++ paps-0.6.6/src/test_libpaps.c 2006-07-03 18:48:04.000000000 +0900
|
||||
+++ paps-0.6.6/src/test_libpaps.c 2006-07-17 13:27:59.000000000 +0900
|
||||
@@ -83,6 +83,7 @@
|
||||
|
||||
ps_layout = paps_layout_to_postscript_strdup(paps,
|
||||
|
@ -1,6 +1,6 @@
|
||||
diff -ruN paps-0.6.6.orig/configure.in paps-0.6.6/configure.in
|
||||
--- paps-0.6.6.orig/configure.in 2006-04-28 04:41:36.000000000 +0900
|
||||
+++ paps-0.6.6/configure.in 2006-07-04 13:22:12.000000000 +0900
|
||||
+++ paps-0.6.6/configure.in 2006-07-17 13:38:30.000000000 +0900
|
||||
@@ -7,4 +7,17 @@
|
||||
AC_PROG_CC
|
||||
AM_PROG_LIBTOOL
|
||||
@ -20,8 +20,8 @@ diff -ruN paps-0.6.6.orig/configure.in paps-0.6.6/configure.in
|
||||
+
|
||||
AC_OUTPUT(Makefile src/Makefile doc/Makefile)
|
||||
diff -ruN paps-0.6.6.orig/src/Makefile.am paps-0.6.6/src/Makefile.am
|
||||
--- paps-0.6.6.orig/src/Makefile.am 2006-07-04 13:21:56.000000000 +0900
|
||||
+++ paps-0.6.6/src/Makefile.am 2006-07-04 13:22:12.000000000 +0900
|
||||
--- paps-0.6.6.orig/src/Makefile.am 2006-07-17 13:35:07.000000000 +0900
|
||||
+++ paps-0.6.6/src/Makefile.am 2006-07-17 13:38:30.000000000 +0900
|
||||
@@ -5,10 +5,10 @@
|
||||
libpaps_a_incdir = $(includedir)
|
||||
|
||||
@ -36,8 +36,8 @@ diff -ruN paps-0.6.6.orig/src/Makefile.am paps-0.6.6/src/Makefile.am
|
||||
|
||||
EXTRA_DIST = test_libpaps.c paps.1
|
||||
diff -ruN paps-0.6.6.orig/src/paps.c paps-0.6.6/src/paps.c
|
||||
--- paps-0.6.6.orig/src/paps.c 2006-07-04 13:21:56.000000000 +0900
|
||||
+++ paps-0.6.6/src/paps.c 2006-07-04 13:23:28.000000000 +0900
|
||||
--- paps-0.6.6.orig/src/paps.c 2006-07-17 13:35:07.000000000 +0900
|
||||
+++ paps-0.6.6/src/paps.c 2006-07-17 13:45:45.000000000 +0900
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <time.h>
|
||||
#include <locale.h>
|
||||
@ -46,7 +46,15 @@ diff -ruN paps-0.6.6.orig/src/paps.c paps-0.6.6/src/paps.c
|
||||
|
||||
#define BUFSIZE 1024
|
||||
#define DEFAULT_FONT_FAMILY "Monospace"
|
||||
@@ -313,8 +314,8 @@
|
||||
@@ -87,6 +88,7 @@
|
||||
PangoDirection pango_dir;
|
||||
gchar *filename;
|
||||
gchar *header_font_desc;
|
||||
+ gchar *owner;
|
||||
} page_layout_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -313,8 +315,8 @@
|
||||
int num_pages = 1;
|
||||
int gutter_width = 40;
|
||||
int total_gutter_width;
|
||||
@ -57,11 +65,12 @@ diff -ruN paps-0.6.6.orig/src/paps.c paps-0.6.6/src/paps.c
|
||||
int do_tumble = -1; /* -1 means not initialized */
|
||||
int do_duplex = -1;
|
||||
gchar *paps_header = NULL;
|
||||
@@ -323,12 +324,113 @@
|
||||
@@ -323,12 +325,118 @@
|
||||
int header_sep = 20;
|
||||
int max_width = 0, w;
|
||||
GIConv cvh = NULL;
|
||||
+ gboolean cups_mode = FALSE;
|
||||
+ gchar *page_owner = NULL;
|
||||
|
||||
/* Prerequisite when using glib. */
|
||||
g_type_init();
|
||||
@ -96,6 +105,7 @@ diff -ruN paps-0.6.6.orig/src/paps.c paps-0.6.6/src/paps.c
|
||||
+ }
|
||||
+ }
|
||||
+ title = argv[3];
|
||||
+ page_owner = argv[2];
|
||||
+ num_options = cupsParseOptions(argv[5], 0, &options);
|
||||
+
|
||||
+ if ((val = cupsGetOption("prettyprint", num_options, options)) != NULL &&
|
||||
@ -162,7 +172,10 @@ diff -ruN paps-0.6.6.orig/src/paps.c paps-0.6.6/src/paps.c
|
||||
+ lpi = atof(val);
|
||||
+ }
|
||||
+ if (getenv("CHARSET") != NULL)
|
||||
+ encoding = getenv("CHARSET");
|
||||
+ encoding = g_strdup(getenv("CHARSET"));
|
||||
+ if ((val = cupsGetOption("document-charset", num_options, options)) != NULL) {
|
||||
+ encoding = g_strdup(val);
|
||||
+ }
|
||||
+ }
|
||||
+ } G_STMT_END;
|
||||
+
|
||||
@ -171,7 +184,7 @@ diff -ruN paps-0.6.6.orig/src/paps.c paps-0.6.6/src/paps.c
|
||||
/* Parse command line */
|
||||
if (!g_option_context_parse(ctxt, &argc, &argv, &error))
|
||||
{
|
||||
@@ -355,6 +457,8 @@
|
||||
@@ -355,6 +463,8 @@
|
||||
IN = stdin;
|
||||
}
|
||||
title = filename_in;
|
||||
@ -180,7 +193,7 @@ diff -ruN paps-0.6.6.orig/src/paps.c paps-0.6.6/src/paps.c
|
||||
|
||||
paps = paps_new();
|
||||
pango_context = paps_get_pango_context (paps);
|
||||
@@ -373,8 +477,10 @@
|
||||
@@ -373,8 +483,10 @@
|
||||
pango_context_set_font_description (pango_context, font_description);
|
||||
|
||||
/* Page layout */
|
||||
@ -193,3 +206,52 @@ diff -ruN paps-0.6.6.orig/src/paps.c paps-0.6.6/src/paps.c
|
||||
|
||||
if (num_columns == 1)
|
||||
total_gutter_width = 0;
|
||||
@@ -435,6 +547,7 @@
|
||||
page_layout.pango_dir = pango_dir;
|
||||
page_layout.filename = filename_in;
|
||||
page_layout.header_font_desc = header_font_desc;
|
||||
+ page_layout.owner = page_owner;
|
||||
|
||||
/* calculate x-coordinate scale */
|
||||
if (cpi > 0.0L)
|
||||
@@ -781,6 +894,7 @@
|
||||
int orientation = page_layout->page_width > page_layout->page_height;
|
||||
int bb_page_width = page_layout->page_width;
|
||||
int bb_page_height = page_layout->page_height;
|
||||
+ char *owner = NULL;
|
||||
|
||||
/* Keep bounding box non-rotated to make ggv happy */
|
||||
if (orientation)
|
||||
@@ -790,12 +904,20 @@
|
||||
bb_page_height = tmp;
|
||||
}
|
||||
|
||||
+ if (page_layout->owner)
|
||||
+ {
|
||||
+ owner = g_strdup_printf("%%%%For: %s\n", page_layout->owner);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ owner = g_strdup("");
|
||||
+ }
|
||||
fprintf(OUT,
|
||||
"%%!PS-Adobe-3.0\n"
|
||||
"%%%%Title: %s\n"
|
||||
"%%%%Creator: paps version 0.6.3 by Dov Grobgeld\n"
|
||||
"%%%%Pages: (atend)\n"
|
||||
- "%%%%BoundingBox: 0 0 %d %d\n"
|
||||
+ "%%%%BoundingBox: 0 0 %d %d\n%s"
|
||||
"%%%%BeginProlog\n"
|
||||
"%%%%Orientation: %s\n"
|
||||
"/papsdict 1 dict def\n"
|
||||
@@ -845,8 +967,10 @@
|
||||
title,
|
||||
bb_page_width,
|
||||
bb_page_height,
|
||||
+ owner,
|
||||
orientation_names[orientation]
|
||||
);
|
||||
+ g_free(owner);
|
||||
|
||||
fprintf(OUT,
|
||||
"%% User settings\n"
|
||||
|
@ -1,6 +1,6 @@
|
||||
Name: paps
|
||||
Version: 0.6.6
|
||||
Release: 10.2%{?dist}
|
||||
Release: 11%{?dist}
|
||||
|
||||
License: LGPL
|
||||
URL: http://paps.sourceforge.net/
|
||||
@ -76,6 +76,10 @@ rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
|
||||
%changelog
|
||||
* Mon Jul 17 2006 Akira TAGOH <tagoh@redhat.com> - 0.6.6-11
|
||||
- add an owner info to PS.
|
||||
- support "document-charset" attribute.
|
||||
|
||||
* Wed Jul 12 2006 Jesse Keating <jkeating@redhat.com> - 0.6.6-10.2
|
||||
- rebuild
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user