paps.c (paps-0.6.8) | : | paps.c (paps-0.7.1) | ||
---|---|---|---|---|
skipping to change at line 25 | skipping to change at line 25 | |||
* | * | |||
* You should have received a copy of the GNU Library General Public | * You should have received a copy of the GNU Library General Public | |||
* License along with this library; if not, write to the | * License along with this library; if not, write to the | |||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330, | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |||
* Boston, MA 02111-1307, USA. | * Boston, MA 02111-1307, USA. | |||
* | * | |||
*/ | */ | |||
#include <pango/pango.h> | #include <pango/pango.h> | |||
#include <pango/pangoft2.h> | #include <pango/pangoft2.h> | |||
#include "libpaps.h" | #include <pango/pangocairo.h> | |||
#include <cairo/cairo.h> | ||||
#include <cairo/cairo-ps.h> | ||||
#include <cairo/cairo-pdf.h> | ||||
#include <cairo/cairo-svg.h> | ||||
#include <errno.h> | #include <errno.h> | |||
#include <langinfo.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | |||
#include <stdio.h> | #include <stdio.h> | |||
#include <string.h> | #include <string.h> | |||
#include <time.h> | #include <time.h> | |||
#include <locale.h> | #include <locale.h> | |||
#include <math.h> | ||||
#include <wchar.h> | ||||
#include <libgen.h> | ||||
#include <config.h> | ||||
#if ENABLE_NLS | ||||
#include <libintl.h> | ||||
#define _(str) gettext(str) | ||||
#ifdef gettext_noop | ||||
#define N_(str) gettext_noop(str) | ||||
#else | ||||
#define N_(str) (str) | ||||
#endif | ||||
#else /* NLS is disabled */ | ||||
#define _(str) (str) | ||||
#define N_(str) (str) | ||||
#endif | ||||
#define BUFSIZE 1024 | #define BUFSIZE 1024 | |||
#define DEFAULT_FONT_FAMILY "Monospace" | #define DEFAULT_FONT_FAMILY "Monospace" | |||
#define DEFAULT_FONT_SIZE "12" | #define DEFAULT_FONT_SIZE "12" | |||
#define HEADER_FONT_FAMILY "Monospace Bold" | #define HEADER_FONT_FAMILY "Monospace Bold" | |||
#define HEADER_FONT_SCALE "12" | #define HEADER_FONT_SCALE "12" | |||
#define MAKE_FONT_NAME(f,s) f " " s | #define MAKE_FONT_NAME(f,s) f " " s | |||
/* | ||||
* Cairo sets limit on the comment line for cairo_ps_surface_dsc_comment() to | ||||
* 255 characters, including the initial percent characters. | ||||
*/ | ||||
#define CAIRO_COMMENT_MAX 255 | ||||
#define MARGIN_LEFT 36 | ||||
#define MARGIN_RIGHT 36 | ||||
#define MARGIN_TOP 36 | ||||
#define MARGIN_BOTTOM 36 | ||||
typedef enum { | typedef enum { | |||
PAPER_TYPE_A4 = 0, | PAPER_TYPE_A4 = 0, | |||
PAPER_TYPE_US_LETTER = 1, | PAPER_TYPE_US_LETTER = 1, | |||
PAPER_TYPE_US_LEGAL = 2 | PAPER_TYPE_US_LEGAL = 2, | |||
PAPER_TYPE_A3 = 3 | ||||
} paper_type_t ; | } paper_type_t ; | |||
typedef enum { | ||||
FORMAT_POSTSCRIPT = 0, | ||||
FORMAT_PDF = 1, | ||||
FORMAT_SVG = 2 | ||||
} output_format_t ; | ||||
typedef struct { | typedef struct { | |||
double width; | double width; | |||
double height; | double height; | |||
} paper_size_t; | } paper_size_t; | |||
const paper_size_t paper_sizes[] = { | static const paper_size_t paper_sizes[] = { | |||
{ 595.28, 841.89}, /* A4 */ | { 595.28, 841.89}, /* A4 */ | |||
{ 612, 792}, /* US letter */ | { 612, 792}, /* US letter */ | |||
{ 612, 1008} /* US legal */ | { 612, 1008}, /* US legal */ | |||
{ 842, 1190} /* A3 */ | ||||
}; | }; | |||
typedef struct { | typedef struct { | |||
double pt_to_pixel; | ||||
double pixel_to_pt; | ||||
int column_width; | int column_width; | |||
int column_height; | int column_height; | |||
int num_columns; | int num_columns; | |||
int gutter_width; /* These are all in postscript points=1/72 inch... */ | int gutter_width; /* These are all in postscript points=1/72 inch... */ | |||
int top_margin; | int top_margin; | |||
int bottom_margin; | int bottom_margin; | |||
int left_margin; | int left_margin; | |||
int right_margin; | int right_margin; | |||
int page_width; | double page_width; | |||
int page_height; | double page_height; | |||
int header_ypos; | int header_ypos; | |||
int header_sep; | int header_sep; | |||
int header_height; | int header_height; | |||
int footer_height; | int footer_height; | |||
gdouble scale_x; | gdouble scale_x; | |||
gdouble scale_y; | gdouble scale_y; | |||
gboolean do_draw_header; | gboolean do_draw_header; | |||
gboolean do_draw_footer; | gboolean do_draw_footer; | |||
gboolean do_duplex; | gboolean do_duplex; | |||
gboolean do_tumble; | gboolean do_tumble; | |||
gboolean do_landscape; | gboolean do_landscape; | |||
gboolean do_justify; | gboolean do_justify; | |||
gboolean do_separation_line; | gboolean do_separation_line; | |||
gboolean do_draw_contour; | gboolean do_draw_contour; | |||
gboolean do_wordwrap; | gboolean do_show_wrap; | |||
gboolean do_use_markup; | gboolean do_use_markup; | |||
gboolean do_stretch_chars; | gboolean do_stretch_chars; | |||
PangoDirection pango_dir; | PangoDirection pango_dir; | |||
gchar *filename; | const gchar *title; | |||
gchar *header_font_desc; | const gchar *header_font_desc; | |||
gint lpi; | gdouble lpi; | |||
gint cpi; | gdouble cpi; | |||
} page_layout_t; | } page_layout_t; | |||
typedef struct { | typedef struct { | |||
char *text; | ||||
int length; | ||||
} para_t; | ||||
typedef struct { | ||||
PangoLayoutLine *pango_line; | PangoLayoutLine *pango_line; | |||
PangoRectangle logical_rect; | PangoRectangle logical_rect; | |||
PangoRectangle ink_rect; | PangoRectangle ink_rect; | |||
int formfeed; | int formfeed; | |||
gboolean wrapped; // Whether the paragraph was character wrapped | ||||
} LineLink; | } LineLink; | |||
typedef struct _Paragraph Paragraph; | typedef struct _Paragraph Paragraph; | |||
/* Structure representing a paragraph | /* Structure representing a paragraph | |||
*/ | */ | |||
struct _Paragraph { | struct _Paragraph { | |||
char *text; | const char *text; | |||
int length; | int length; | |||
int height; /* Height, in pixels */ | int height; /* Height, in pixels */ | |||
int formfeed; | int formfeed; | |||
gboolean wrapped; | ||||
gboolean clipped; // Whether the line was clipped. Used for CPI. | ||||
PangoLayout *layout; | PangoLayout *layout; | |||
}; | }; | |||
/* Information passed in user data when drawing outlines */ | /* Information passed in user data when drawing outlines */ | |||
GList *split_paragraphs_into_lines (page_layout_t *page_layout, | static GList *split_paragraphs_into_lines (page_layout_t *page_layout, | |||
GList *paragraphs); | GList *paragraphs); | |||
static char *read_file (FILE *file, | static char *read_file (FILE *file, | |||
GIConv handle); | gchar *encoding); | |||
static GList *split_text_into_paragraphs (PangoContext *pango_context, | static GList *split_text_into_paragraphs (cairo_t *cr, | |||
PangoContext *pango_context, | ||||
page_layout_t *page_layout, | page_layout_t *page_layout, | |||
int paint_width, | int paint_width, | |||
char *text); | const char *text); | |||
static int output_pages (FILE *OUT, | static int output_pages (cairo_surface_t * surface, | |||
cairo_t *cr, | ||||
GList *pango_lines, | GList *pango_lines, | |||
page_layout_t *page_layout, | page_layout_t *page_layout, | |||
gboolean need_header, | gboolean need_header, | |||
PangoContext *pango_context); | PangoContext *pango_context); | |||
static void print_postscript_header (FILE *OUT, | static void eject_column (cairo_t *cr, | |||
const char *title, | ||||
page_layout_t *page_layout); | ||||
static void print_postscript_trailer (FILE *OUT, | ||||
int num_pages); | ||||
static void eject_column (FILE *OUT, | ||||
page_layout_t *page_layout, | page_layout_t *page_layout, | |||
int column_idx); | int column_idx); | |||
static void eject_page (FILE *OUT); | static void eject_page (cairo_t *cr); | |||
static void start_page (FILE *OUT, | static void start_page (cairo_surface_t *surface, | |||
int page_idx); | cairo_t *cr, | |||
static void draw_line_to_page (FILE *OUT, | page_layout_t *page_layout); | |||
static void draw_line_to_page (cairo_t *cr, | ||||
int column_idx, | int column_idx, | |||
int column_pos, | int column_pos, | |||
page_layout_t *page_layout, | page_layout_t *page_layout, | |||
PangoLayoutLine *line); | PangoLayoutLine *line, | |||
static int draw_page_header_line_to_page(FILE *OUT, | gboolean draw_wrap_character | |||
); | ||||
static int draw_page_header_line_to_page(cairo_t *cr, | ||||
gboolean is_footer, | gboolean is_footer, | |||
page_layout_t *page_layout, | page_layout_t *page_layout, | |||
PangoContext *ctx, | PangoContext *ctx, | |||
int page); | int page); | |||
static void postscript_dsc_comments (cairo_surface_t *surface, | ||||
page_layout_t *page_layout); | ||||
// Fonts are three character symbols in an alphabet composing of | FILE *output_fh; | |||
// the following characters: | static paper_type_t paper_type = PAPER_TYPE_A4; | |||
// | static gboolean output_format_set = FALSE; | |||
// First character: a-zA-Z@_.,!-~`'" | static output_format_t output_format = FORMAT_POSTSCRIPT; | |||
// Rest of chars: like first + 0-9 | static PangoGravity gravity = PANGO_GRAVITY_AUTO; | |||
// | static PangoGravityHint gravity_hint = PANGO_GRAVITY_HINT_NATURAL; | |||
// Care is taken that no keywords are overwritten, e.g. def, end. | static PangoWrapMode opt_wrap = PANGO_WRAP_WORD_CHAR; | |||
GString *ps_font_def_string = NULL; | static cairo_font_face_t *paps_glyph_face = NULL; /* Special face for paps chara | |||
GString *ps_pages_string = NULL; | cters, e.g. newline */ | |||
int last_char_idx = 0; | static double glyph_font_size = -1; | |||
double last_pos_y = -1; | ||||
double last_pos_x = -1; | /* Render function for paps glyphs */ | |||
paps_t *paps; | static cairo_status_t | |||
paper_type_t paper_type = PAPER_TYPE_A4; | paps_render_glyph(cairo_scaled_font_t *scaled_font G_GNUC_UNUSED, | |||
unsigned long glyph, | ||||
cairo_t *cr, | ||||
cairo_text_extents_t *extents) | ||||
{ | ||||
char ch = (unsigned char)glyph; | ||||
#define CASE(s) if (strcmp(S_, s) == 0) | if (ch == 'R' || ch == 'L') | |||
{ | ||||
// A newline sign that I created with MetaPost | ||||
cairo_save(cr); | ||||
cairo_scale(cr,0.005,-0.005); // TBD - figure out the scaling. | ||||
if (ch == 'L') | ||||
{ | ||||
cairo_scale(cr,-1,1); | ||||
// cairo_translate(cr,-120,0); // Keep glyph protruding to the right. | ||||
} | ||||
cairo_translate(cr, 20,-50); | ||||
cairo_move_to(cr, 0, 175); | ||||
cairo_curve_to(cr, 25.69278, 175, 53.912, 177.59557, 71.25053, 158.75053); | ||||
cairo_curve_to(cr, 103.52599, 123.67075, 64.54437, 77.19373, 34.99985, 34.99 | ||||
985); | ||||
cairo_set_line_width(cr, 25); | ||||
cairo_stroke(cr); | ||||
cairo_move_to(cr,0,0); | ||||
cairo_line_to(cr,75,0); | ||||
cairo_line_to(cr,0,75); | ||||
cairo_close_path(cr); | ||||
cairo_fill(cr); | ||||
cairo_restore(cr); | ||||
} | ||||
return CAIRO_STATUS_SUCCESS; | ||||
} | ||||
static gboolean | static gboolean | |||
_paps_arg_paper_cb(const char *option_name, | _paps_arg_paper_cb(const char *option_name, | |||
const char *value, | const char *value, | |||
gpointer data) | gpointer data) | |||
{ | { | |||
gboolean retval = TRUE; | gboolean retval = TRUE; | |||
if (value && *value) | if (value && *value) | |||
{ | { | |||
if (g_ascii_strcasecmp(value, "legal") == 0) | if (g_ascii_strcasecmp(value, "legal") == 0) | |||
paper_type = PAPER_TYPE_US_LEGAL; | paper_type = PAPER_TYPE_US_LEGAL; | |||
else if (g_ascii_strcasecmp(value, "letter") == 0) | else if (g_ascii_strcasecmp(value, "letter") == 0) | |||
paper_type = PAPER_TYPE_US_LETTER; | paper_type = PAPER_TYPE_US_LETTER; | |||
else if (g_ascii_strcasecmp(value, "a4") == 0) | else if (g_ascii_strcasecmp(value, "a4") == 0) | |||
paper_type = PAPER_TYPE_A4; | paper_type = PAPER_TYPE_A4; | |||
else if (g_ascii_strcasecmp(value, "a3") == 0) | ||||
paper_type = PAPER_TYPE_A3; | ||||
else { | else { | |||
retval = FALSE; | retval = FALSE; | |||
fprintf(stderr, "Unknown page size name: %s.\n", value); | fprintf(stderr, _("Unknown page size name: %s.\n"), value); | |||
} | } | |||
} | } | |||
else | else | |||
{ | { | |||
fprintf(stderr, "You must specify page size.\n"); | fprintf(stderr, _("You must specify page size.\n")); | |||
retval = FALSE; | ||||
} | ||||
return retval; | ||||
} | ||||
static gboolean | ||||
parse_enum (GType type, | ||||
int *value, | ||||
const char *name, | ||||
const char *arg, | ||||
gpointer data G_GNUC_UNUSED, | ||||
GError **error) | ||||
{ | ||||
char *possible_values = NULL; | ||||
gboolean ret; | ||||
ret = pango_parse_enum (type, | ||||
arg, | ||||
value, | ||||
FALSE, | ||||
&possible_values); | ||||
if (!ret && error) | ||||
{ | ||||
g_set_error(error, | ||||
G_OPTION_ERROR, | ||||
G_OPTION_ERROR_BAD_VALUE, | ||||
_("Argument for %1$s must be one of %2$s"), | ||||
name, | ||||
possible_values); | ||||
} | ||||
g_free (possible_values); | ||||
return ret; | ||||
} | ||||
static gboolean | ||||
parse_wrap (const char *name, | ||||
const char *arg, | ||||
gpointer data, | ||||
GError **error) | ||||
{ | ||||
return (parse_enum (PANGO_TYPE_WRAP_MODE, (int*)(void*)&opt_wrap, | ||||
name, arg, data, error)); | ||||
} | ||||
static gboolean | ||||
parse_gravity_hint (const char *name, | ||||
const char *arg, | ||||
gpointer data, | ||||
GError **error) | ||||
{ | ||||
return (parse_enum (PANGO_TYPE_GRAVITY_HINT, (int*)(void*)&gravity_hint, | ||||
name, arg, data, error)); | ||||
} | ||||
static gboolean | ||||
parse_gravity (const char *name, | ||||
const char *arg, | ||||
gpointer data, | ||||
GError **error) | ||||
{ | ||||
return (parse_enum (PANGO_TYPE_GRAVITY, (int*)(void*)&gravity, | ||||
name, arg, data, error)); | ||||
} | ||||
static gboolean | ||||
_paps_arg_format_cb(const char *option_name, | ||||
const char *value, | ||||
gpointer data) | ||||
{ | ||||
gboolean retval = TRUE; | ||||
if (value && *value) | ||||
{ | ||||
output_format_set = TRUE; | ||||
if (g_ascii_strcasecmp(value, "pdf") == 0) | ||||
output_format = FORMAT_PDF; | ||||
else if (g_ascii_strcasecmp(value, "ps") == 0 | ||||
|| g_ascii_strcasecmp(value, "postscript") == 0) | ||||
output_format = FORMAT_POSTSCRIPT; | ||||
else if (g_ascii_strcasecmp(value, "svg") == 0) | ||||
output_format = FORMAT_SVG; | ||||
else { | ||||
retval = FALSE; | ||||
fprintf(stderr, _("Unknown output format: %s.\n"), value); | ||||
} | ||||
} | ||||
else | ||||
{ | ||||
fprintf(stderr, _("You must specify a output format.\n")); | ||||
retval = FALSE; | retval = FALSE; | |||
} | } | |||
return retval; | return retval; | |||
} | } | |||
static gboolean | static gboolean | |||
_paps_arg_lpi_cb(const gchar *option_name, | _paps_arg_lpi_cb(const gchar *option_name, | |||
const gchar *value, | const gchar *value, | |||
gpointer data) | gpointer data) | |||
{ | { | |||
gboolean retval = TRUE; | gboolean retval = TRUE; | |||
gchar *p = NULL; | gchar *p = NULL; | |||
page_layout_t *page_layout = (page_layout_t*)data; | page_layout_t *page_layout = (page_layout_t*)data; | |||
if (value && *value) | if (value && *value) | |||
{ | { | |||
errno = 0; | errno = 0; | |||
page_layout->lpi = g_strtod(value, &p); | page_layout->lpi = g_strtod(value, &p); | |||
if ((p && *p) || errno == ERANGE) | if ((p && *p) || errno == ERANGE) | |||
{ | { | |||
fprintf(stderr, "given LPI value was invalid.\n"); | fprintf(stderr, _("Given LPI value was invalid.\n")); | |||
retval = FALSE; | retval = FALSE; | |||
} | } | |||
} | } | |||
else | else | |||
{ | { | |||
fprintf(stderr, "You must specify the amount of lines per inch.\n"); | fprintf(stderr, _("You must specify the amount of lines per inch.\n")); | |||
retval = FALSE; | retval = FALSE; | |||
} | } | |||
return retval; | return retval; | |||
} | } | |||
static gboolean | static gboolean | |||
_paps_arg_cpi_cb(const gchar *option_name, | _paps_arg_cpi_cb(const gchar *option_name, | |||
const gchar *value, | const gchar *value, | |||
gpointer data) | gpointer data) | |||
{ | { | |||
gboolean retval = TRUE; | gboolean retval = TRUE; | |||
gchar *p = NULL; | gchar *p = NULL; | |||
page_layout_t *page_layout = (page_layout_t*)data; | page_layout_t *page_layout = (page_layout_t*)data; | |||
if (value && *value) | if (value && *value) | |||
{ | { | |||
errno = 0; | errno = 0; | |||
page_layout->cpi = g_strtod(value, &p); | page_layout->cpi = g_strtod(value, &p); | |||
if ((p && *p) || errno == ERANGE) | if ((p && *p) || errno == ERANGE) | |||
{ | { | |||
fprintf(stderr, "given CPI value was invalid.\n"); | fprintf(stderr, _("Given CPI value was invalid.\n")); | |||
retval = FALSE; | retval = FALSE; | |||
} | } | |||
} | } | |||
else | else | |||
{ | { | |||
fprintf(stderr, "You must specify the amount of characters per inch.\n"); | fprintf(stderr, _("You must specify the amount of characters per inch.\n") ); | |||
retval = FALSE; | retval = FALSE; | |||
} | } | |||
return retval; | return retval; | |||
} | } | |||
static PangoLanguage * | /* | |||
get_language(void) | * Return codeset name of the environment's locale. Use UTF8 by default | |||
*/ | ||||
static char* | ||||
get_encoding() | ||||
{ | { | |||
PangoLanguage *retval; | static char *encoding = NULL; | |||
gchar *lang = g_strdup (setlocale (LC_CTYPE, NULL)); | ||||
gchar *p; | ||||
p = strchr (lang, '.'); | ||||
if (p) | ||||
*p = 0; | ||||
p = strchr (lang, '@'); | ||||
if (p) | ||||
*p = 0; | ||||
retval = pango_language_from_string (lang); | if (encoding == NULL) | |||
g_free (lang); | encoding = nl_langinfo(CODESET); | |||
return retval; | return encoding; | |||
} | ||||
static cairo_status_t paps_cairo_write_func(void *closure G_GNUC_UNUSED, | ||||
const unsigned char *data, | ||||
unsigned int length) | ||||
{ | ||||
fwrite(data,length,1,output_fh); | ||||
return CAIRO_STATUS_SUCCESS; | ||||
} | } | |||
int main(int argc, char *argv[]) | int main(int argc, char *argv[]) | |||
{ | { | |||
gboolean do_landscape = FALSE, do_rtl = FALSE, do_justify = FALSE, do_draw_hea der = FALSE; | gboolean do_landscape = FALSE, do_rtl = FALSE, do_justify = FALSE, do_draw_hea der = FALSE, do_draw_footer=FALSE; | |||
gboolean do_stretch_chars = FALSE; | gboolean do_stretch_chars = FALSE; | |||
gboolean do_use_markup = FALSE; | gboolean do_use_markup = FALSE; | |||
gboolean do_wordwrap = TRUE; // What should be default? | gboolean do_show_wrap = FALSE; /* Whether to show wrap characters */ | |||
int num_columns = 1; | int num_columns = 1; | |||
int top_margin = 36, bottom_margin = 36, right_margin = 36, left_margin = 36; | int top_margin = MARGIN_TOP, bottom_margin = MARGIN_BOTTOM, | |||
gchar *font = MAKE_FONT_NAME (DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE), *encodi | right_margin = MARGIN_RIGHT, left_margin = MARGIN_LEFT; | |||
ng = NULL; | ||||
gboolean do_fatal_warnings = FALSE; | ||||
const gchar *font = MAKE_FONT_NAME (DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE); | ||||
gchar *encoding = NULL; | ||||
gchar *output = NULL; | ||||
gchar *htitle = NULL; | ||||
page_layout_t page_layout; | page_layout_t page_layout; | |||
GOptionContext *ctxt = g_option_context_new("[text file]"); | GOptionContext *ctxt = g_option_context_new("[text file]"); | |||
GOptionEntry entries[] = { | GOptionEntry entries[] = { | |||
{"landscape", 0, 0, G_OPTION_ARG_NONE, &do_landscape, "Landscape output. (De | {"landscape", 0, 0, G_OPTION_ARG_NONE, &do_landscape, | |||
fault: portrait)", NULL}, | N_("Landscape output. (Default: portrait)"), NULL}, | |||
{"stretch-chars", 0, 0, G_OPTION_ARG_NONE, &do_stretch_chars, "Whether to st | {"columns", 0, 0, G_OPTION_ARG_INT, &num_columns, | |||
retch characters in y-direction to fill lines. (Default: no)", NULL}, | N_("Number of columns output. (Default: 1)"), "NUM"}, | |||
{"markup", 0, 0, G_OPTION_ARG_NONE, &do_use_markup, "Should the text be cons | {"font", 0, 0, G_OPTION_ARG_STRING, &font, | |||
idered pango markup? (Default: no)", NULL}, | N_("Set font. (Default: Monospace 12)"), "DESC"}, | |||
{"columns", 0, 0, G_OPTION_ARG_INT, &num_columns, "Number of columns output. | {"output", 'o', 0, G_OPTION_ARG_STRING, &output, | |||
(Default: 1)", "NUM"}, | N_("Output file. (Default: stdout)"), "DESC"}, | |||
{"font", 0, 0, G_OPTION_ARG_STRING, &font, "Set the font description. (Defau | {"rtl", 0, 0, G_OPTION_ARG_NONE, &do_rtl, | |||
lt: Monospace 12)", "DESC"}, | N_("Do right-to-left text layout."), NULL}, | |||
{"rtl", 0, 0, G_OPTION_ARG_NONE, &do_rtl, "Do rtl layout.", NULL}, | {"justify", 0, 0, G_OPTION_ARG_NONE, &do_justify, | |||
N_("Justify the layout."), NULL}, | ||||
{"wrap", 0, 0, G_OPTION_ARG_CALLBACK, &parse_wrap, | ||||
N_("Text wrapping mode [word, char, word-char]. (Default: word-char)"), "WR | ||||
AP"}, | ||||
{"show-wrap", 0, 0, G_OPTION_ARG_NONE, &do_show_wrap, | ||||
N_("Show characters for wrapping."), NULL}, | ||||
{"paper", 0, 0, G_OPTION_ARG_CALLBACK, _paps_arg_paper_cb, | {"paper", 0, 0, G_OPTION_ARG_CALLBACK, _paps_arg_paper_cb, | |||
"Choose paper size. Known paper sizes are legal,\n" | N_("Set paper size [legal, letter, a3, a4]. (Default: a4)"), "PAPER"}, | |||
" letter, a4. (Default: a4)", "PAPER"}, | {"gravity", 0, 0, G_OPTION_ARG_CALLBACK, &parse_gravity, | |||
{"bottom-margin", 0, 0, G_OPTION_ARG_INT, &bottom_margin, "Set bottom margin | N_("Base glyph rotation [south, west, north, east, auto]. (Defaut: auto)"), | |||
in postscript point units (1/72inch). (Default: 36)", "NUM"}, | "GRAVITY"}, | |||
{"top-margin", 0, 0, G_OPTION_ARG_INT, &top_margin, "Set top margin. (Defaul | {"gravity-hint", 0, 0, G_OPTION_ARG_CALLBACK, &parse_gravity_hint, | |||
t: 36)", "NUM"}, | N_("Base glyph orientation [natural, strong, line]. (Default: natural)"), " | |||
{"right-margin", 0, 0, G_OPTION_ARG_INT, &right_margin, "Set right margin. ( | HINT"}, | |||
Default: 36)", "NUM"}, | {"format", 0, 0, G_OPTION_ARG_CALLBACK, _paps_arg_format_cb, | |||
{"left-margin", 0, 0, G_OPTION_ARG_INT, &left_margin, "Set left margin. (Def | N_("Set output format [pdf, svg, ps]. (Default: ps)"), "FORMAT"}, | |||
ault: 36)", "NUM"}, | {"bottom-margin", 0, 0, G_OPTION_ARG_INT, &bottom_margin, | |||
{"header", 0, 0, G_OPTION_ARG_NONE, &do_draw_header, "Draw page header for e | N_("Set bottom margin in postscript point units (1/72 inch). (Default: 36)" | |||
ach page.", NULL}, | ), "NUM"}, | |||
{"encoding", 0, 0, G_OPTION_ARG_STRING, &encoding, "Assume the documentation | {"top-margin", 0, 0, G_OPTION_ARG_INT, &top_margin, | |||
encoding.", "ENCODING"}, | N_("Set top margin. (Default: 36)"), "NUM"}, | |||
{"lpi", 0, 0, G_OPTION_ARG_CALLBACK, _paps_arg_lpi_cb, "Set the amount of li | {"right-margin", 0, 0, G_OPTION_ARG_INT, &right_margin, | |||
nes per inch.", "REAL"}, | N_("Set right margin. (Default: 36)"), "NUM"}, | |||
{"cpi", 0, 0, G_OPTION_ARG_CALLBACK, _paps_arg_cpi_cb, "Set the amount of ch | {"left-margin", 0, 0, G_OPTION_ARG_INT, &left_margin, | |||
aracters per inch.", "REAL"}, | N_("Set left margin. (Default: 36)"), "NUM"}, | |||
{"header", 0, 0, G_OPTION_ARG_NONE, &do_draw_header, | ||||
N_("Draw page header for each page."), NULL}, | ||||
{"footer", 0, 0, G_OPTION_ARG_NONE, &do_draw_footer, | ||||
"Draw page footer for each page.", NULL}, | ||||
{"title", 0, 0, G_OPTION_ARG_STRING, &htitle, | ||||
N_("Title string for page header (Default: filename/stdin)."), "TITLE"}, | ||||
{"markup", 0, 0, G_OPTION_ARG_NONE, &do_use_markup, | ||||
N_("Interpret input text as pango markup."), NULL}, | ||||
{"encoding", 0, 0, G_OPTION_ARG_STRING, &encoding, | ||||
N_("Assume encoding of input text. (Default: UTF-8)"), "ENCODING"}, | ||||
{"lpi", 0, 0, G_OPTION_ARG_CALLBACK, _paps_arg_lpi_cb, | ||||
N_("Set the amount of lines per inch."), "REAL"}, | ||||
{"cpi", 0, 0, G_OPTION_ARG_CALLBACK, _paps_arg_cpi_cb, | ||||
N_("Set the amount of characters per inch."), "REAL"}, | ||||
/* | ||||
* not fixed for cairo backend: disable | ||||
* | ||||
{"stretch-chars", 0, 0, G_OPTION_ARG_NONE, &do_stretch_chars, | ||||
N_("Stretch characters in y-direction to fill lines."), NULL}, | ||||
*/ | ||||
{"g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &do_fatal_warnings, | ||||
N_("Make all glib warnings fatal."), "REAL"}, | ||||
{NULL} | {NULL} | |||
}; | }; | |||
GError *error = NULL; | GError *error = NULL; | |||
FILE *IN, *OUT = NULL; | FILE *IN = NULL; | |||
GList *paragraphs; | GList *paragraphs; | |||
GList *pango_lines; | GList *pango_lines; | |||
PangoContext *pango_context; | PangoContext *pango_context; | |||
PangoFontDescription *font_description; | PangoFontDescription *font_description; | |||
PangoDirection pango_dir = PANGO_DIRECTION_LTR; | PangoDirection pango_dir = PANGO_DIRECTION_LTR; | |||
PangoFontMap *fontmap; | PangoFontMap *fontmap; | |||
PangoFontset *fontset; | PangoFontset *fontset; | |||
PangoFontMetrics *metrics; | PangoFontMetrics *metrics; | |||
int num_pages = 1; | ||||
int gutter_width = 40; | int gutter_width = 40; | |||
int total_gutter_width; | int total_gutter_width; | |||
int page_width = paper_sizes[0].width; | double page_width = paper_sizes[0].width; | |||
int page_height = paper_sizes[0].height; | double page_height = paper_sizes[0].height; | |||
int do_tumble = -1; /* -1 means not initialized */ | int do_tumble = -1; /* -1 means not initialized */ | |||
int do_duplex = -1; | int do_duplex = -1; | |||
gchar *paps_header = NULL; | const gchar *header_font_desc = MAKE_FONT_NAME (HEADER_FONT_FAMILY, HEADER_FON | |||
gchar *header_font_desc = MAKE_FONT_NAME (HEADER_FONT_FAMILY, HEADER_FONT_SCAL | T_SCALE); | |||
E); | const gchar *filename_in; | |||
gchar *filename_in, *title, *text; | gchar *text; | |||
int header_sep = 20; | int header_sep = 20; | |||
int max_width = 0, w; | int max_width = 0, w; | |||
GIConv cvh = NULL; | ||||
GOptionGroup *options; | GOptionGroup *options; | |||
cairo_t *cr; | ||||
/* Prerequisite when using glib. */ | cairo_surface_t *surface = NULL; | |||
g_type_init(); | double surface_page_width = 0, surface_page_height = 0; | |||
/* Set locale from environment */ | ||||
(void) setlocale(LC_ALL, ""); | ||||
/* Setup i18n */ | ||||
textdomain(GETTEXT_PACKAGE); | ||||
bindtextdomain(GETTEXT_PACKAGE, DATADIR "/locale"); | ||||
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); | ||||
/* Setup the paps glyph face */ | ||||
paps_glyph_face = cairo_user_font_face_create(); | ||||
cairo_user_font_face_set_render_glyph_func(paps_glyph_face, paps_render_glyph) | ||||
; | ||||
/* Init page_layout_t parameters set by the option parsing */ | /* Init page_layout_t parameters set by the option parsing */ | |||
page_layout.cpi = page_layout.lpi = 0; | page_layout.cpi = page_layout.lpi = 0; | |||
options = g_option_group_new("main","","",&page_layout, NULL); | options = g_option_group_new("main","","",&page_layout, NULL); | |||
g_option_group_add_entries(options, entries); | g_option_group_add_entries(options, entries); | |||
g_option_group_set_translation_domain(options, GETTEXT_PACKAGE); | ||||
g_option_context_set_main_group(ctxt, options); | g_option_context_set_main_group(ctxt, options); | |||
#if 0 | #if 0 | |||
g_option_context_add_main_entries(ctxt, entries, NULL); | g_option_context_add_main_entries(ctxt, entries, NULL); | |||
#endif | #endif | |||
/* Parse command line */ | /* Parse command line */ | |||
if (!g_option_context_parse(ctxt, &argc, &argv, &error)) | if (!g_option_context_parse(ctxt, &argc, &argv, &error)) | |||
{ | { | |||
fprintf(stderr, "Command line error: %s\n", error->message); | fprintf(stderr, _("Command line error: %s\n"), error->message); | |||
exit(1); | exit(1); | |||
} | } | |||
if (do_fatal_warnings) | ||||
g_log_set_always_fatal(G_LOG_LEVEL_MASK); | ||||
if (do_rtl) | if (do_rtl) | |||
pango_dir = PANGO_DIRECTION_RTL; | pango_dir = PANGO_DIRECTION_RTL; | |||
if (argc > 1) | if (argc > 1) | |||
{ | { | |||
filename_in = argv[1]; | filename_in = argv[1]; | |||
IN = fopen(filename_in, "rb"); | IN = fopen(filename_in, "r"); | |||
if (!IN) | if (!IN) | |||
{ | { | |||
fprintf(stderr, "Failed to open %s!\n", filename_in); | fprintf(stderr, _("Failed to open %s!\n"), filename_in); | |||
exit(-1); | exit(1); | |||
} | } | |||
} | } | |||
else | else | |||
{ | { | |||
filename_in = "stdin"; | filename_in = "stdin"; | |||
IN = stdin; | IN = stdin; | |||
} | } | |||
title = filename_in; | ||||
paps = paps_new(); | // For now always write to stdout | |||
pango_context = paps_get_pango_context (paps); | if (output == NULL) | |||
output_fh = stdout; | ||||
else | ||||
{ | ||||
output_fh = fopen(output,"wb"); | ||||
if (!output_fh) | ||||
{ | ||||
fprintf(stderr, _("Failed to open %s for writing!\n"), output); | ||||
exit(1); | ||||
} | ||||
} | ||||
/* Page layout */ | ||||
page_width = paper_sizes[(int)paper_type].width; | ||||
page_height = paper_sizes[(int)paper_type].height; | ||||
/* Deduce output format from file name if not explicitely set */ | ||||
if (!output_format_set && output != NULL) | ||||
{ | ||||
if (g_str_has_suffix(output, ".svg") || g_str_has_suffix(output, ".SVG")) | ||||
output_format = FORMAT_SVG; | ||||
else if (g_str_has_suffix(output, ".pdf") || g_str_has_suffix(output, ".PD | ||||
F")) | ||||
output_format = FORMAT_PDF; | ||||
/* Otherwise keep postscript default */ | ||||
} | ||||
/* Swap width and height for landscape except for postscript */ | ||||
surface_page_width = page_width; | ||||
surface_page_height = page_height; | ||||
if (output_format != FORMAT_POSTSCRIPT && do_landscape) | ||||
{ | ||||
surface_page_width = page_height; | ||||
surface_page_height = page_width; | ||||
} | ||||
if (output_format == FORMAT_POSTSCRIPT) | ||||
surface = cairo_ps_surface_create_for_stream(&paps_cairo_write_func, | ||||
NULL, | ||||
surface_page_width, | ||||
surface_page_height); | ||||
else if (output_format == FORMAT_PDF) | ||||
surface = cairo_pdf_surface_create_for_stream(&paps_cairo_write_func, | ||||
NULL, | ||||
surface_page_width, | ||||
surface_page_height); | ||||
else | ||||
surface = cairo_svg_surface_create_for_stream(&paps_cairo_write_func, | ||||
NULL, | ||||
surface_page_width, | ||||
surface_page_height); | ||||
cr = cairo_create(surface); | ||||
pango_context = pango_cairo_create_context(cr); | ||||
pango_cairo_context_set_resolution(pango_context, 72.0); /* Native postscript | ||||
resolution */ | ||||
/* Setup pango */ | /* Setup pango */ | |||
pango_context_set_language (pango_context, get_language ()); | ||||
pango_context_set_base_dir (pango_context, pango_dir); | pango_context_set_base_dir (pango_context, pango_dir); | |||
pango_context_set_language (pango_context, pango_language_get_default ()); | ||||
pango_context_set_base_gravity (pango_context, gravity); | ||||
pango_context_set_gravity_hint (pango_context, gravity_hint); | ||||
/* create the font description */ | /* create the font description */ | |||
font_description = pango_font_description_from_string (font); | font_description = pango_font_description_from_string (font); | |||
if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MAS K_FAMILY) == 0) | if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MAS K_FAMILY) == 0) | |||
pango_font_description_set_family (font_description, DEFAULT_FONT_FAMILY); | pango_font_description_set_family (font_description, DEFAULT_FONT_FAMILY); | |||
if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MAS K_SIZE) == 0) | if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MAS K_SIZE) == 0) | |||
pango_font_description_set_size (font_description, atoi(DEFAULT_FONT_SIZE) * PANGO_SCALE); | pango_font_description_set_size (font_description, atoi(DEFAULT_FONT_SIZE) * PANGO_SCALE); | |||
// Keep the font size for the wrap character. | ||||
glyph_font_size = pango_font_description_get_size(font_description) / PANGO_SC | ||||
ALE; | ||||
pango_context_set_font_description (pango_context, font_description); | pango_context_set_font_description (pango_context, font_description); | |||
/* Page layout */ | if (num_columns <= 0) { | |||
page_width = paper_sizes[(int)paper_type].width; | fprintf(stderr, _("%s: Invalid input: --columns=%d, using default.\n"), g_ge | |||
page_height = paper_sizes[(int)paper_type].height; | t_prgname (), num_columns); | |||
num_columns = 1; | ||||
} | ||||
if (num_columns == 1) | if (num_columns == 1) | |||
total_gutter_width = 0; | total_gutter_width = 0; | |||
else | else | |||
total_gutter_width = gutter_width * (num_columns - 1); | total_gutter_width = gutter_width * (num_columns - 1); | |||
if (do_landscape) | if (do_landscape) | |||
{ | { | |||
int tmp; | double tmp; | |||
tmp = page_width; | tmp = page_width; | |||
page_width = page_height; | page_width = page_height; | |||
page_height = tmp; | page_height = tmp; | |||
if (do_tumble < 0) | if (do_tumble < 0) | |||
do_tumble = TRUE; | do_tumble = TRUE; | |||
if (do_duplex < 0) | if (do_duplex < 0) | |||
do_duplex = TRUE; | do_duplex = TRUE; | |||
} | } | |||
else | else | |||
{ | { | |||
skipping to change at line 427 | skipping to change at line 709 | |||
page_layout.page_height = page_height; | page_layout.page_height = page_height; | |||
page_layout.num_columns = num_columns; | page_layout.num_columns = num_columns; | |||
page_layout.left_margin = left_margin; | page_layout.left_margin = left_margin; | |||
page_layout.right_margin = right_margin; | page_layout.right_margin = right_margin; | |||
page_layout.gutter_width = gutter_width; | page_layout.gutter_width = gutter_width; | |||
page_layout.top_margin = top_margin; | page_layout.top_margin = top_margin; | |||
page_layout.bottom_margin = bottom_margin; | page_layout.bottom_margin = bottom_margin; | |||
page_layout.header_ypos = page_layout.top_margin; | page_layout.header_ypos = page_layout.top_margin; | |||
page_layout.header_height = 0; | page_layout.header_height = 0; | |||
page_layout.footer_height = 0; | page_layout.footer_height = 0; | |||
page_layout.do_wordwrap = do_wordwrap; | page_layout.do_show_wrap = do_show_wrap; | |||
page_layout.scale_x = 1.0L; | page_layout.scale_x = 1.0L; | |||
page_layout.scale_y = 1.0L; | page_layout.scale_y = 1.0L; | |||
if (do_draw_header) | if (do_draw_header) | |||
page_layout.header_sep = header_sep; | page_layout.header_sep = 0; // header_sep; | |||
else | else | |||
page_layout.header_sep = 0; | page_layout.header_sep = 0; | |||
page_layout.column_height = page_height | page_layout.column_height = (int)page_height | |||
- page_layout.top_margin | - page_layout.top_margin | |||
- page_layout.header_sep | - page_layout.header_sep | |||
- page_layout.bottom_margin; | - page_layout.bottom_margin; | |||
page_layout.column_width = (page_layout.page_width | page_layout.column_width = ((int)page_layout.page_width | |||
- page_layout.left_margin - page_layout.right_margin | - page_layout.left_margin - page_layout.right_margin | |||
- total_gutter_width) / page_layout.num_columns; | - total_gutter_width) / page_layout.num_columns; | |||
page_layout.pt_to_pixel = paps_postscript_points_to_pango(1)/PANGO_SCALE; | ||||
page_layout.pixel_to_pt = 1.0/page_layout.pt_to_pixel; | ||||
page_layout.do_separation_line = TRUE; | page_layout.do_separation_line = TRUE; | |||
page_layout.do_landscape = do_landscape; | page_layout.do_landscape = do_landscape; | |||
page_layout.do_justify = do_justify; | page_layout.do_justify = do_justify; | |||
page_layout.do_stretch_chars = do_stretch_chars; | page_layout.do_stretch_chars = do_stretch_chars; | |||
page_layout.do_use_markup = do_use_markup; | page_layout.do_use_markup = do_use_markup; | |||
page_layout.do_tumble = do_tumble; | page_layout.do_tumble = do_tumble; | |||
page_layout.do_duplex = do_duplex; | page_layout.do_duplex = do_duplex; | |||
page_layout.pango_dir = pango_dir; | page_layout.pango_dir = pango_dir; | |||
page_layout.filename = filename_in; | if (htitle) | |||
page_layout.title = htitle; | ||||
else | ||||
page_layout.title = basename((char *)filename_in); | ||||
page_layout.header_font_desc = header_font_desc; | page_layout.header_font_desc = header_font_desc; | |||
/* calculate x-coordinate scale */ | /* calculate x-coordinate scale */ | |||
if (page_layout.cpi > 0.0L) | if (page_layout.cpi > 0.0L) | |||
{ | { | |||
double scale; | gint font_size; | |||
fontmap = pango_ft2_font_map_new (); | fontmap = pango_ft2_font_map_new (); | |||
fontset = pango_font_map_load_fontset (fontmap, pango_context, font_descri ption, get_language ()); | fontset = pango_font_map_load_fontset (fontmap, pango_context, font_descri ption, pango_language_get_default()); | |||
metrics = pango_fontset_get_metrics (fontset); | metrics = pango_fontset_get_metrics (fontset); | |||
max_width = pango_font_metrics_get_approximate_char_width (metrics); | max_width = pango_font_metrics_get_approximate_char_width (metrics); | |||
w = pango_font_metrics_get_approximate_digit_width (metrics); | w = pango_font_metrics_get_approximate_digit_width (metrics); | |||
if (w > max_width) | if (w > max_width) | |||
max_width = w; | max_width = w; | |||
page_layout.scale_x = 1 / page_layout.cpi * 72.0 * PANGO_SCALE / max_width | page_layout.scale_x = 1 / page_layout.cpi * 72.0 * (gdouble)PANGO_SCALE / | |||
; | (gdouble)max_width; | |||
pango_font_metrics_unref (metrics); | pango_font_metrics_unref (metrics); | |||
g_object_unref (G_OBJECT (fontmap)); | g_object_unref (G_OBJECT (fontmap)); | |||
// Now figure out how to scale the font to get that size | font_size = pango_font_description_get_size (font_description); | |||
scale = 1 / page_layout.cpi * 72.0 * PANGO_SCALE / max_width; | ||||
// update the font size to that width | // update the font size to that width | |||
pango_font_description_set_size (font_description, (int)(atoi(DEFAULT_FONT | pango_font_description_set_size (font_description, (int)(font_size * page_ | |||
_SIZE) * PANGO_SCALE * scale)); | layout.scale_x)); | |||
glyph_font_size = font_size * page_layout.scale_x / PANGO_SCALE; | ||||
pango_context_set_font_description (pango_context, font_description); | pango_context_set_font_description (pango_context, font_description); | |||
} | } | |||
page_layout.scale_x = page_layout.scale_y = 1.0; | page_layout.scale_x = page_layout.scale_y = 1.0; | |||
if (encoding != NULL) | if (encoding == NULL) | |||
{ | encoding = get_encoding(); | |||
cvh = g_iconv_open ("UTF-8", encoding); | ||||
if (cvh == NULL) | ||||
{ | ||||
fprintf(stderr, "%s: Invalid encoding: %s\n", g_get_prgname (), encodi | ||||
ng); | ||||
exit(-1); | ||||
} | ||||
} | ||||
text = read_file(IN, cvh); | text = read_file(IN, encoding); | |||
if (encoding != NULL && cvh != NULL) | if (output_format == FORMAT_POSTSCRIPT) | |||
g_iconv_close(cvh); | postscript_dsc_comments(surface, &page_layout); | |||
paragraphs = split_text_into_paragraphs(pango_context, | paragraphs = split_text_into_paragraphs(cr, | |||
pango_context, | ||||
&page_layout, | &page_layout, | |||
page_layout.column_width * page_layout .pt_to_pixel, | page_layout.column_width, | |||
text); | text); | |||
pango_lines = split_paragraphs_into_lines(&page_layout, paragraphs); | pango_lines = split_paragraphs_into_lines(&page_layout, paragraphs); | |||
if (OUT == NULL) | cairo_scale(cr, page_layout.scale_x, page_layout.scale_y); | |||
OUT = stdout; | ||||
paps_set_scale(paps, page_layout.scale_x, page_layout.scale_y); | ||||
print_postscript_header(OUT, title, &page_layout); | ||||
ps_pages_string = g_string_new(""); | ||||
num_pages = output_pages(OUT, pango_lines, &page_layout, do_draw_header, pango | ||||
_context); | ||||
paps_header = paps_get_postscript_header_strdup(paps); | ||||
fprintf(OUT, "%s", paps_header); | ||||
g_free(paps_header); | ||||
fprintf(OUT, "%%%%EndPrologue\n"); | output_pages(surface, cr, pango_lines, &page_layout, do_draw_header, pango_con | |||
fprintf(OUT, "%s", ps_pages_string->str); | text); | |||
print_postscript_trailer(OUT, num_pages); | ||||
// Cleanup | cairo_destroy (cr); | |||
g_string_free(ps_pages_string, TRUE); | cairo_surface_finish (surface); | |||
cairo_surface_destroy(surface); | ||||
g_option_context_free(ctxt); | g_option_context_free(ctxt); | |||
return 0; | return 0; | |||
} | } | |||
/* Read an entire file into a string | /* Read an entire file into a string | |||
*/ | */ | |||
static char * | static char * | |||
read_file (FILE *file, | read_file (FILE *file, | |||
GIConv handle) | gchar *encoding) | |||
{ | { | |||
GString *inbuf; | GString *inbuf; | |||
char *text; | char *text; | |||
char buffer[BUFSIZE]; | char buffer[BUFSIZE]; | |||
GIConv cvh = NULL; | ||||
gsize inc_seq_bytes = 0; | ||||
if (encoding != NULL) | ||||
{ | ||||
cvh = g_iconv_open ("UTF-8", encoding); | ||||
if (cvh == (GIConv)-1) | ||||
{ | ||||
fprintf(stderr, _("%s: Invalid encoding: %s\n"), g_get_prgname (), enc | ||||
oding); | ||||
exit(1); | ||||
} | ||||
} | ||||
inbuf = g_string_new (NULL); | inbuf = g_string_new (NULL); | |||
while (1) | while (1) | |||
{ | { | |||
char *ib, *ob, obuffer[BUFSIZE * 6], *bp = fgets (buffer, BUFSIZE-1, file) | char *ib, *ob, obuffer[BUFSIZE * 6], *bp; | |||
; | gsize iblen, ibleft, oblen; | |||
gsize iblen, oblen; | ||||
bp = fgets (buffer+inc_seq_bytes, BUFSIZE-inc_seq_bytes-1, file); | ||||
if (inc_seq_bytes) | ||||
inc_seq_bytes = 0; | ||||
if (ferror (file)) | if (ferror (file)) | |||
{ | { | |||
fprintf(stderr, "%s: Error reading file.\n", g_get_prgname ()); | fprintf(stderr, _("%s: Error reading file.\n"), g_get_prgname ()); | |||
g_string_free (inbuf, TRUE); | g_string_free (inbuf, TRUE); | |||
return NULL; | exit(1); | |||
} | } | |||
else if (bp == NULL) | else if (bp == NULL) | |||
break; | break; | |||
if (handle != NULL) | if (cvh != NULL) | |||
{ | { | |||
ib = bp; | ib = buffer; | |||
iblen = strlen (ib); | iblen = strlen (ib); | |||
ob = bp = obuffer; | ob = bp = obuffer; | |||
oblen = BUFSIZE * 6 - 1; | oblen = BUFSIZE * 6 - 1; | |||
if (g_iconv (handle, &ib, &iblen, &ob, &oblen) == -1) | if (g_iconv (cvh, &ib, &iblen, &ob, &oblen) == (gsize)-1) | |||
{ | { | |||
fprintf (stderr, "%s: Error while converting strings.\n", g_get_pr | /* | |||
gname ()); | * EINVAL - incomplete sequence at the end of the buffer. Move the | |||
return NULL; | * incomplete sequence bytes to the beginning of the buffer for | |||
} | * the next round of conversion. | |||
*/ | ||||
if (errno == EINVAL) | ||||
{ | ||||
inc_seq_bytes = iblen; | ||||
memmove (buffer, ib, inc_seq_bytes); | ||||
} | ||||
else | ||||
{ | ||||
fprintf (stderr, _("%1$s: Error while converting input from '% | ||||
2$s' to UTF-8.\n"), | ||||
g_get_prgname(), encoding); | ||||
exit(1); | ||||
} | ||||
} | ||||
obuffer[BUFSIZE * 6 - 1 - oblen] = 0; | obuffer[BUFSIZE * 6 - 1 - oblen] = 0; | |||
} | } | |||
g_string_append (inbuf, bp); | g_string_append (inbuf, bp); | |||
} | } | |||
fclose (file); | fclose (file); | |||
/* Add a trailing new line if it is missing */ | /* Add a trailing new line if it is missing */ | |||
if (inbuf->str[inbuf->len-1] != '\n') | if (inbuf->len && inbuf->str[inbuf->len-1] != '\n') | |||
g_string_append(inbuf, "\n"); | g_string_append(inbuf, "\n"); | |||
text = inbuf->str; | text = inbuf->str; | |||
g_string_free (inbuf, FALSE); | g_string_free (inbuf, FALSE); | |||
return text; | if (encoding != NULL && cvh != NULL) | |||
} | g_iconv_close(cvh); | |||
#if 0 | ||||
/* Take a UTF8 string and break it into paragraphs on \n characters. | ||||
* | ||||
* Sorry. I couldn't figure out what this version was supposed to do | ||||
* | ||||
*/ | ||||
static GList * | ||||
split_text_into_paragraphs (PangoContext *pango_context, | ||||
page_layout_t *page_layout, | ||||
int paint_width, /* In pixels */ | ||||
char *text) | ||||
{ | ||||
char *p = text; | ||||
char *next; | ||||
gunichar wc; | ||||
GList *result = NULL; | ||||
char *last_para = text; | ||||
while (p != NULL && *p) | ||||
{ | ||||
wc = g_utf8_get_char (p); | ||||
next = g_utf8_next_char (p); | ||||
if (wc == (gunichar)-1) | ||||
{ | ||||
fprintf (stderr, "%s: Invalid character in input\n", g_get_prgname ()) | ||||
; | ||||
wc = 0; | ||||
} | ||||
if (!*p || !wc || wc == '\n' || wc == '\f') | ||||
{ | ||||
Paragraph *para = g_new (Paragraph, 1); | ||||
para->text = last_para; | ||||
para->length = p - last_para; | ||||
para->layout = pango_layout_new (pango_context); | ||||
if (cpi > 0.0L && page_layout->do_wordwrap) | ||||
{ | ||||
PangoRectangle ink_rect, logical_rect; | ||||
wchar_t *wtext, *wnewtext; | ||||
gchar *newtext; | ||||
size_t i, len, wwidth = 0, n; | ||||
wtext = g_utf8_to_ucs4 (para->text, para->length, NULL, NULL, NULL) | ||||
; | ||||
if (wtext == NULL) | ||||
{ | ||||
fprintf (stderr, "Failed to convert UTF-8 to UCS-4.\n"); | ||||
return NULL; | ||||
} | ||||
len = g_utf8_strlen (para->text, para->length); | ||||
/* the amount of characters to be able to put on the line against C | ||||
PI */ | ||||
n = page_layout->column_width / 72.0 * cpi; | ||||
if (len > n) | ||||
{ | ||||
wnewtext = g_new (wchar_t, wcslen (wtext) + 1); | ||||
if (wnewtext == NULL) | ||||
{ | ||||
fprintf (stderr, "Failed to allocate a memory.\n"); | ||||
g_free (wtext); | ||||
return NULL; | ||||
} | ||||
for (i = 0; i < len; i++) | ||||
{ | ||||
wwidth += wcwidth (wtext[i]); | ||||
if (wwidth > n) | ||||
break; | ||||
wnewtext[i] = wtext[i]; | ||||
} | ||||
wnewtext[i] = 0L; | ||||
newtext = g_ucs4_to_utf8 ((const gunichar *)wnewtext, i, NULL, | ||||
NULL, NULL); | ||||
if (newtext == NULL) | ||||
{ | ||||
fprintf (stderr, "Failed to convert UCS-4 to UTF-8.\n"); | ||||
return NULL; | ||||
} | ||||
pango_layout_set_text (para->layout, newtext, -1); | ||||
pango_layout_get_extents (para->layout, &ink_rect, &logical_rec | ||||
t); | ||||
/* update paint_width to wrap_against CPI */ | ||||
paint_width = logical_rect.width / PANGO_SCALE; | ||||
g_free (newtext); | ||||
g_free (wnewtext); | ||||
} | ||||
g_free (wtext); | ||||
} | ||||
pango_layout_set_text (para->layout, para->text, para->length); | ||||
pango_layout_set_justify (para->layout, page_layout->do_justify); | ||||
pango_layout_set_alignment (para->layout, | ||||
page_layout->pango_dir == PANGO_DIRECTION_ | ||||
LTR | ||||
? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT); | ||||
pango_layout_set_width (para->layout, paint_width * PANGO_SCALE); | ||||
if (page_layout->do_wordwrap) | ||||
pango_layout_set_wrap (para->layout, PANGO_WRAP_WORD_CHAR); | ||||
para->height = 0; | ||||
if (wc == '\f') | ||||
para->formfeed = 1; | ||||
else | ||||
para->formfeed = 0; | ||||
last_para = next; | ||||
result = g_list_prepend (result, para); | ||||
} | ||||
if (!wc) /* incomplete character at end */ | ||||
break; | ||||
p = next; | ||||
} | ||||
return g_list_reverse (result); | return text; | |||
} | } | |||
#endif | ||||
/* Take a UTF8 string and break it into paragraphs on \n characters | /* Take a UTF8 string and break it into paragraphs on \n characters | |||
*/ | */ | |||
static GList * | static GList * | |||
split_text_into_paragraphs (PangoContext *pango_context, | split_text_into_paragraphs (cairo_t *cr, | |||
PangoContext *pango_context, | ||||
page_layout_t *page_layout, | page_layout_t *page_layout, | |||
int paint_width, /* In pixels */ | int paint_width, /* In pixels */ | |||
char *text) | const char *text) | |||
{ | { | |||
char *p = text; | const char *p = text; | |||
char *next; | char *next; | |||
gunichar wc; | gunichar wc; | |||
GList *result = NULL; | GList *result = NULL; | |||
char *last_para = text; | const char *last_para = text; | |||
/* If we are using markup we treat the entire text as a single paragraph. | /* If we are using markup we treat the entire text as a single paragraph. | |||
* I tested it and found that this is much slower than the split and | * I tested it and found that this is much slower than the split and | |||
* assign method used below. Otherwise we might as well use this single | * assign method used below. Otherwise we might as well use this single | |||
* chunk method always. | * chunk method always. | |||
*/ | */ | |||
if (page_layout->do_use_markup) | if (page_layout->do_use_markup) | |||
{ | { | |||
Paragraph *para = g_new (Paragraph, 1); | Paragraph *para = g_new (Paragraph, 1); | |||
para->wrapped = FALSE; /* No wrapped chars for markups */ | ||||
para->clipped = FALSE; | ||||
para->text = text; | para->text = text; | |||
para->length = strlen(text); | para->length = strlen(text); | |||
para->layout = pango_layout_new (pango_context); | para->layout = pango_layout_new (pango_context); | |||
// pango_layout_set_font_description (para->layout, font_descript ion); | ||||
pango_layout_set_markup (para->layout, para->text, para->length); | pango_layout_set_markup (para->layout, para->text, para->length); | |||
pango_layout_set_justify (para->layout, page_layout->do_justify); | pango_layout_set_justify (para->layout, page_layout->do_justify); | |||
pango_layout_set_alignment (para->layout, | pango_layout_set_alignment (para->layout, | |||
page_layout->pango_dir == PANGO_DIRECTION_LTR | page_layout->pango_dir == PANGO_DIRECTION_LTR | |||
? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT); | ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT); | |||
pango_layout_set_wrap (para->layout, PANGO_WRAP_WORD_CHAR); | ||||
pango_layout_set_width (para->layout, paint_width * PANGO_SCALE); | pango_layout_set_width (para->layout, paint_width * PANGO_SCALE); | |||
pango_layout_set_wrap (para->layout, opt_wrap); | ||||
para->height = 0; | para->height = 0; | |||
result = g_list_prepend (result, para); | result = g_list_prepend (result, para); | |||
} | } | |||
else | else | |||
{ | { | |||
while (p != NULL && *p) | while (p != NULL && *p) | |||
{ | { | |||
wc = g_utf8_get_char (p); | wc = g_utf8_get_char (p); | |||
next = g_utf8_next_char (p); | next = g_utf8_next_char (p); | |||
if (wc == (gunichar)-1) | if (wc == (gunichar)-1) | |||
{ | { | |||
fprintf (stderr, "%s: Invalid character in input\n", g_get_prgname ()); | fprintf (stderr, _("%s: Invalid character in input\n"), g_get_prgn ame ()); | |||
wc = 0; | wc = 0; | |||
} | } | |||
if (!*p || !wc || wc == '\n' || wc == '\f') | if (!*p || !wc || wc == '\r' || wc == '\n' || wc == '\f') | |||
{ | { | |||
Paragraph *para = g_new (Paragraph, 1); | Paragraph *para = g_new (Paragraph, 1); | |||
para->wrapped = FALSE; | ||||
para->clipped = FALSE; | ||||
para->text = last_para; | para->text = last_para; | |||
para->length = p - last_para; | para->length = p - last_para; | |||
/* handle dos line breaks */ | ||||
if (wc == '\r' && *next == '\n') | ||||
next = g_utf8_next_char(next); | ||||
para->layout = pango_layout_new (pango_context); | para->layout = pango_layout_new (pango_context); | |||
// pango_layout_set_font_description (para->layout, font_ | if (page_layout->cpi > 0.0L) | |||
description); | { | |||
pango_layout_set_text (para->layout, para->text, para->length); | /* figuring out the correct width from the pango_font_metrics_ | |||
get_approximate_width() | ||||
* is really hard and pango_layout_set_wrap() doesn't work pro | ||||
perly then. | ||||
* Those are not reliable to render the characters exactly acc | ||||
ording to the given CPI. | ||||
* So re-calculate the width to wrap up to be comfortable with | ||||
CPI. | ||||
*/ | ||||
wchar_t *wtext = NULL, *wnewtext = NULL; | ||||
gchar *newtext = NULL; | ||||
gsize len, col, i, wwidth = 0; | ||||
PangoRectangle ink_rect, logical_rect; | ||||
wtext = (wchar_t *)g_utf8_to_ucs4 (para->text, para->length, N | ||||
ULL, NULL, NULL); | ||||
if (wtext == NULL) | ||||
{ | ||||
fprintf (stderr, _("%s: Unable to convert UTF-8 to UCS-4.\ | ||||
n"), g_get_prgname ()); | ||||
fail: | ||||
g_free (wtext); | ||||
g_free (wnewtext); | ||||
g_free (newtext); | ||||
exit (1); | ||||
} | ||||
len = g_utf8_strlen (para->text, para->length); | ||||
/* the amount of characters that can be put on the line agains | ||||
t CPI */ | ||||
col = (int)(page_layout->column_width / 72.0 * page_layout->cp | ||||
i); | ||||
if (len > col) | ||||
{ | ||||
/* need to wrap them up */ | ||||
wnewtext = g_new (wchar_t, wcslen (wtext) + 1); | ||||
para->clipped = TRUE; | ||||
if (wnewtext == NULL) | ||||
{ | ||||
fprintf (stderr, _("%s: Unable to allocate the memory. | ||||
\n"), g_get_prgname ()); | ||||
goto fail; | ||||
} | ||||
for (i = 0; i < len; i++) | ||||
{ | ||||
gssize w = wcwidth (wtext[i]); | ||||
if (w >= 0) | ||||
wwidth += w; | ||||
if (wwidth > col) | ||||
break; | ||||
wnewtext[i] = wtext[i]; | ||||
} | ||||
wnewtext[i] = 0L; | ||||
newtext = g_ucs4_to_utf8 ((const gunichar *)wnewtext, i, N | ||||
ULL, NULL, NULL); | ||||
if (newtext == NULL) | ||||
{ | ||||
fprintf (stderr, _("%s: Unable to convert UCS-4 to UTF | ||||
-8.\n"), g_get_prgname ()); | ||||
goto fail; | ||||
} | ||||
pango_layout_set_text (para->layout, newtext, -1); | ||||
pango_layout_get_extents (para->layout, &ink_rect, &logica | ||||
l_rect); | ||||
paint_width = logical_rect.width / PANGO_SCALE; | ||||
g_free (wnewtext); | ||||
g_free (newtext); | ||||
para->length = i; | ||||
next = g_utf8_offset_to_pointer (para->text, para->length) | ||||
; | ||||
wc = g_utf8_get_char (g_utf8_prev_char (next)); | ||||
} | ||||
else | ||||
{ | ||||
pango_layout_set_text (para->layout, para->text, para->len | ||||
gth); | ||||
} | ||||
g_free (wtext); | ||||
pango_layout_set_width (para->layout, -1); | ||||
} | ||||
else | ||||
{ | ||||
pango_layout_set_text (para->layout, para->text, para->length) | ||||
; | ||||
pango_layout_set_width (para->layout, paint_width * PANGO_SCAL | ||||
E); | ||||
pango_layout_set_wrap (para->layout, opt_wrap); | ||||
if (opt_wrap == PANGO_WRAP_CHAR) | ||||
para->wrapped = TRUE; | ||||
/* Should we support truncation as well? */ | ||||
} | ||||
pango_layout_set_justify (para->layout, page_layout->do_justify); | pango_layout_set_justify (para->layout, page_layout->do_justify); | |||
pango_layout_set_alignment (para->layout, | pango_layout_set_alignment (para->layout, | |||
page_layout->pango_dir == PANGO_DIRECT ION_LTR | page_layout->pango_dir == PANGO_DIRECT ION_LTR | |||
? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT ); | ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT ); | |||
pango_layout_set_wrap (para->layout, PANGO_WRAP_WORD_CHAR); | ||||
pango_layout_set_width (para->layout, paint_width * PANGO_SCALE); | ||||
para->height = 0; | para->height = 0; | |||
last_para = next; | last_para = next; | |||
if (wc == '\f') | if (wc == '\f') | |||
para->formfeed = 1; | para->formfeed = 1; | |||
else | else | |||
para->formfeed = 0; | para->formfeed = 0; | |||
result = g_list_prepend (result, para); | result = g_list_prepend (result, para); | |||
skipping to change at line 781 | skipping to change at line 1054 | |||
} | } | |||
} | } | |||
return g_list_reverse (result); | return g_list_reverse (result); | |||
} | } | |||
/* Split a list of paragraphs into a list of lines. | /* Split a list of paragraphs into a list of lines. | |||
*/ | */ | |||
GList * | GList * | |||
split_paragraphs_into_lines(page_layout_t *page_layout, | split_paragraphs_into_lines(page_layout_t *page_layout, | |||
GList *paragraphs) | GList *paragraphs) | |||
{ | { | |||
GList *line_list = NULL; | GList *line_list = NULL; | |||
int max_height = 0; | int max_height = 0; | |||
/* Read the file */ | /* Read the file */ | |||
/* Now split all the pagraphs into lines */ | /* Now split all the pagraphs into lines */ | |||
GList *par_list; | GList *par_list; | |||
par_list = paragraphs; | par_list = paragraphs; | |||
while(par_list) | while(par_list) | |||
skipping to change at line 805 | skipping to change at line 1078 | |||
Paragraph *para = par_list->data; | Paragraph *para = par_list->data; | |||
para_num_lines = pango_layout_get_line_count(para->layout); | para_num_lines = pango_layout_get_line_count(para->layout); | |||
for (i=0; i<para_num_lines; i++) | for (i=0; i<para_num_lines; i++) | |||
{ | { | |||
PangoRectangle logical_rect, ink_rect; | PangoRectangle logical_rect, ink_rect; | |||
line_link = g_new(LineLink, 1); | line_link = g_new(LineLink, 1); | |||
line_link->formfeed = 0; | line_link->formfeed = 0; | |||
line_link->wrapped = (para->wrapped && i < para_num_lines - 1) || (par a->clipped); | ||||
line_link->pango_line = pango_layout_get_line(para->layout, i); | line_link->pango_line = pango_layout_get_line(para->layout, i); | |||
pango_layout_line_get_extents(line_link->pango_line, | pango_layout_line_get_extents(line_link->pango_line, | |||
&ink_rect, &logical_rect); | &ink_rect, &logical_rect); | |||
line_link->logical_rect = logical_rect; | line_link->logical_rect = logical_rect; | |||
if (para->formfeed && i == (para_num_lines - 1)) | if (para->formfeed && i == (para_num_lines - 1)) | |||
line_link->formfeed = 1; | line_link->formfeed = 1; | |||
line_link->ink_rect = ink_rect; | line_link->ink_rect = ink_rect; | |||
line_list = g_list_prepend(line_list, line_link); | line_list = g_list_prepend(line_list, line_link); | |||
if (logical_rect.height > max_height) | if (logical_rect.height > max_height) | |||
max_height = logical_rect.height; | max_height = logical_rect.height; | |||
} | } | |||
par_list = par_list->next; | par_list = par_list->next; | |||
} | } | |||
/* | ||||
* not fixed for cairo backend: disable | ||||
* | ||||
if (page_layout->do_stretch_chars && page_layout->lpi > 0.0L) | if (page_layout->do_stretch_chars && page_layout->lpi > 0.0L) | |||
page_layout->scale_y = 1.0 / page_layout->lpi * 72.0 * page_layout->pt_to_ | page_layout->scale_y = 1.0 / page_layout->lpi * 72.0 * PANGO_SCALE / max_h | |||
pixel * PANGO_SCALE / max_height; | eight; | |||
*/ | ||||
return g_list_reverse(line_list); | return g_list_reverse(line_list); | |||
} | } | |||
/* | ||||
* Define PostScript document header information. | ||||
*/ | ||||
void | ||||
postscript_dsc_comments(cairo_surface_t *surface, page_layout_t *pl) | ||||
{ | ||||
char buf[CAIRO_COMMENT_MAX]; | ||||
int x, y; | ||||
/* | ||||
* Title | ||||
*/ | ||||
snprintf(buf, CAIRO_COMMENT_MAX, "%%%%Title: %s", pl->title); | ||||
cairo_ps_surface_dsc_comment (surface, buf); | ||||
/* | ||||
* Orientation | ||||
*/ | ||||
if (pl->do_landscape) | ||||
{ | ||||
cairo_ps_surface_dsc_comment (surface, "%%Orientation: Landscape"); | ||||
x = (int)pl->page_height; | ||||
y = (int)pl->page_width; | ||||
} | ||||
else | ||||
{ | ||||
cairo_ps_surface_dsc_comment (surface, "%%Orientation: Portrait"); | ||||
x = (int)pl->page_width; | ||||
y = (int)pl->page_height; | ||||
} | ||||
/* | ||||
* Redefine BoundingBox to cover the whole paper. Cairo creates the entry | ||||
* based on the text only. This may affect further processing, such as with | ||||
* convert(1). | ||||
*/ | ||||
snprintf(buf, CAIRO_COMMENT_MAX, "%%%%BoundingBox: 0 0 %d %d", x, y); | ||||
cairo_ps_surface_dsc_comment (surface, buf); | ||||
/* | ||||
* Duplex | ||||
*/ | ||||
if (pl->do_duplex) | ||||
{ | ||||
cairo_ps_surface_dsc_comment(surface, "%%Requirements: duplex"); | ||||
cairo_ps_surface_dsc_begin_setup(surface); | ||||
if (pl->do_tumble) | ||||
cairo_ps_surface_dsc_comment(surface, "%%IncludeFeature: *Duplex DuplexT | ||||
umble"); | ||||
else | ||||
cairo_ps_surface_dsc_comment(surface, "%%IncludeFeature: *Duplex DuplexN | ||||
oTumble"); | ||||
} | ||||
} | ||||
int | int | |||
output_pages(FILE *OUT, | output_pages(cairo_surface_t *surface, | |||
cairo_t *cr, | ||||
GList *pango_lines, | GList *pango_lines, | |||
page_layout_t *page_layout, | page_layout_t *page_layout, | |||
gboolean need_header, | gboolean need_header, | |||
PangoContext *pango_context) | PangoContext *pango_context) | |||
{ | { | |||
int column_idx = 0; | int column_idx = 0; | |||
int column_y_pos = 0; | int column_y_pos = 0; | |||
int page_idx = 1; | int page_idx = 1; | |||
int pango_column_height = page_layout->column_height * page_layout->pt_to_pixe | int pango_column_height = page_layout->column_height * PANGO_SCALE; | |||
l * PANGO_SCALE; | int height = 0; | |||
LineLink *prev_line_link = NULL; | LineLink *prev_line_link = NULL; | |||
start_page(OUT, page_idx); | start_page(surface, cr, page_layout); | |||
if (need_header) | if (need_header) | |||
draw_page_header_line_to_page(OUT, FALSE, page_layout, pango_context, page_i dx); | draw_page_header_line_to_page(cr, TRUE, page_layout, pango_context, page_idx ); | |||
while(pango_lines) | while(pango_lines) | |||
{ | { | |||
LineLink *line_link = pango_lines->data; | LineLink *line_link = pango_lines->data; | |||
PangoLayoutLine *line = line_link->pango_line; | PangoLayoutLine *line = line_link->pango_line; | |||
gboolean draw_wrap_character = page_layout->do_show_wrap && line_link->wra pped; | ||||
/* Check if we need to move to next column */ | /* Check if we need to move to next column */ | |||
if ((column_y_pos + line_link->logical_rect.height | if ((column_y_pos + line_link->logical_rect.height | |||
>= pango_column_height) || | >= pango_column_height) || | |||
(prev_line_link && prev_line_link->formfeed)) | (prev_line_link && prev_line_link->formfeed)) | |||
{ | { | |||
column_idx++; | column_idx++; | |||
column_y_pos = 0; | column_y_pos = 0; | |||
if (column_idx == page_layout->num_columns) | if (column_idx == page_layout->num_columns) | |||
{ | { | |||
column_idx = 0; | column_idx = 0; | |||
eject_page(OUT); | eject_page(cr); | |||
page_idx++; | page_idx++; | |||
start_page(OUT, page_idx); | start_page(surface, cr, page_layout); | |||
if (need_header) | if (need_header) | |||
draw_page_header_line_to_page(OUT, FALSE, page_layout, pango_con text, page_idx); | draw_page_header_line_to_page(cr, TRUE, page_layout, pango_conte xt, page_idx); | |||
} | } | |||
else | else | |||
{ | { | |||
eject_column(OUT, | eject_column(cr, | |||
page_layout, | page_layout, | |||
column_idx | column_idx | |||
); | ); | |||
} | } | |||
} | } | |||
draw_line_to_page(OUT, | ||||
column_idx, | ||||
column_y_pos+line_link->logical_rect.height, | ||||
page_layout, | ||||
line); | ||||
if (page_layout->lpi > 0.0L) | if (page_layout->lpi > 0.0L) | |||
column_y_pos += (int)(1.0 / page_layout->lpi * 72.0 * page_layout->pt_to _pixel * PANGO_SCALE); | height = (int)(1.0 / page_layout->lpi * 72.0 * PANGO_SCALE); | |||
else | else | |||
column_y_pos += line_link->logical_rect.height; | height = line_link->logical_rect.height; | |||
draw_line_to_page(cr, | ||||
column_idx, | ||||
column_y_pos+height, | ||||
page_layout, | ||||
line, | ||||
draw_wrap_character); | ||||
column_y_pos += height; | ||||
pango_lines = pango_lines->next; | pango_lines = pango_lines->next; | |||
prev_line_link = line_link; | prev_line_link = line_link; | |||
} | } | |||
eject_page(OUT); | eject_page(cr); | |||
return page_idx; | return page_idx; | |||
} | } | |||
void print_postscript_header(FILE *OUT, | void eject_column(cairo_t *cr, | |||
const char *title, | ||||
page_layout_t *page_layout) | ||||
{ | ||||
const char *bool_name[2] = { "false", "true" }; | ||||
const char *orientation_names[2] = { "Portrait", "Landscape" }; | ||||
int bodytop = page_layout->header_ypos + page_layout->header_sep; | ||||
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; | ||||
/* Keep bounding box non-rotated to make ggv happy */ | ||||
if (orientation) | ||||
{ | ||||
int tmp = bb_page_width; | ||||
bb_page_width = bb_page_height; | ||||
bb_page_height = tmp; | ||||
} | ||||
fprintf(OUT, | ||||
"%%!PS-Adobe-3.0\n" | ||||
"%%%%Title: %s\n" | ||||
"%%%%Creator: paps version 0.6.7 by Dov Grobgeld\n" | ||||
"%%%%Pages: (atend)\n" | ||||
"%%%%BoundingBox: 0 0 %d %d\n" | ||||
"%%%%BeginProlog\n" | ||||
"%%%%Orientation: %s\n" | ||||
"/papsdict 1 dict def\n" | ||||
"papsdict begin\n" | ||||
"\n" | ||||
"/inch {72 mul} bind def\n" | ||||
"/mm {1 inch 25.4 div mul} bind def\n" | ||||
"\n" | ||||
"%% override setpagedevice if it is not defined\n" | ||||
"/setpagedevice where {\n" | ||||
" pop %% get rid of its dictionary\n" | ||||
" /setpagesize { \n" | ||||
" 3 dict begin\n" | ||||
" /pageheight exch def \n" | ||||
" /pagewidth exch def\n" | ||||
" /orientation 0 def\n" | ||||
" %% Exchange pagewidth and pageheight so that pagewidth is bi | ||||
gger\n" | ||||
" pagewidth pageheight gt { \n" | ||||
" pagewidth\n" | ||||
" /pagewidth pageheight def\n" | ||||
" /pageheight exch def\n" | ||||
" /orientation 3 def\n" | ||||
" } if\n" | ||||
" 2 dict\n" | ||||
" dup /PageSize [pagewidth pageheight] put\n" | ||||
" dup /Orientation orientation put\n" | ||||
" setpagedevice \n" | ||||
" end\n" | ||||
" } def\n" | ||||
"}\n" | ||||
"{\n" | ||||
" /setpagesize { pop pop } def\n" | ||||
"} ifelse\n" | ||||
"/duplex {\n" | ||||
" statusdict /setduplexmode known \n" | ||||
" { statusdict begin setduplexmode end } {pop} ifelse\n" | ||||
"} def\n" | ||||
"/tumble {\n" | ||||
" statusdict /settumble known\n" | ||||
" { statusdict begin settumble end } {pop} ifelse\n" | ||||
"} def\n" | ||||
"%% Turn the page around\n" | ||||
"/turnpage {\n" | ||||
" 90 rotate\n" | ||||
" 0 pageheight neg translate\n" | ||||
"} def\n", | ||||
title, | ||||
bb_page_width, | ||||
bb_page_height, | ||||
orientation_names[orientation] | ||||
); | ||||
fprintf(OUT, | ||||
"%% User settings\n" | ||||
"/pagewidth %d def\n" | ||||
"/pageheight %d def\n" | ||||
"pagewidth pageheight setpagesize\n" | ||||
"/column_width %d def\n" | ||||
"/bodyheight %d def\n" | ||||
"/lmarg %d def\n" | ||||
"/ytop %d def\n" | ||||
"/do_separation_line %s def\n" | ||||
"/do_landscape %s def\n" | ||||
"/do_tumble %s def\n" | ||||
"/do_duplex %s def\n", | ||||
page_layout->page_width, | ||||
page_layout->page_height, | ||||
page_layout->column_width, | ||||
page_layout->column_height, | ||||
page_layout->left_margin, | ||||
page_layout->page_height - bodytop, | ||||
bool_name[page_layout->do_separation_line>0], | ||||
bool_name[page_layout->do_landscape>0], | ||||
bool_name[page_layout->do_tumble>0], | ||||
bool_name[page_layout->do_duplex>0] | ||||
); | ||||
fprintf(OUT, | ||||
"%% Procedures to translate position to first and second column\n" | ||||
"/lw 20 def %% whatever\n" | ||||
"/setnumcolumns {\n" | ||||
" /numcolumns exch def\n" | ||||
" /firstcolumn { /xpos lmarg def /ypos ytop def} def\n" | ||||
" /nextcolumn { \n" | ||||
" do_separation_line {\n" | ||||
" xpos column_width add gutter_width 2 div add %% x start\n" | ||||
" ytop lw add moveto %% y start\n" | ||||
" 0 bodyheight lw add neg rlineto 0 setlinewidth stroke\n" | ||||
" } if\n" | ||||
" /xpos xpos column_width add gutter_width add def \n" | ||||
" /ypos ytop def\n" | ||||
" } def\n" | ||||
"} def\n" | ||||
"\n" | ||||
); | ||||
fprintf(OUT, | ||||
"%d setnumcolumns\n", | ||||
page_layout->num_columns); | ||||
fprintf(OUT, | ||||
"/showline {\n" | ||||
" /y exch def\n" | ||||
" /s exch def\n" | ||||
" xpos y moveto \n" | ||||
" column_width 0 rlineto stroke\n" | ||||
" xpos y moveto /Helvetica findfont 20 scalefont setfont s show\n" | ||||
"} def\n" | ||||
); | ||||
// The following definitions polute the global namespace. All such | ||||
// definitions should start with paps_ | ||||
fprintf(OUT, | ||||
"/paps_bop { %% Beginning of page definitions\n" | ||||
" papsdict begin\n" | ||||
" gsave\n" | ||||
" do_landscape {turnpage} if \n" | ||||
" %% ps2pdf gets wrong orientation without this!\n" | ||||
" /Helvetica findfont setfont 100 100 moveto ( ) show\n" | ||||
" firstcolumn\n" | ||||
" end\n" | ||||
"} def\n" | ||||
"\n" | ||||
"/paps_eop { %% End of page cleanups\n" | ||||
" grestore \n" | ||||
"} def\n"); | ||||
} | ||||
void print_postscript_trailer(FILE *OUT, | ||||
int num_pages) | ||||
{ | ||||
fprintf(OUT, | ||||
"%%%%Pages: %d\n" | ||||
"%%%%Trailer\n" | ||||
"%%%%EOF\n", | ||||
num_pages | ||||
); | ||||
} | ||||
void eject_column(FILE *OUT, | ||||
page_layout_t *page_layout, | page_layout_t *page_layout, | |||
int column_idx) | int column_idx) | |||
{ | { | |||
double x_pos, y_top, y_bot, total_gutter; | double x_pos, y_top, y_bot, total_gutter; | |||
#if 0 | #if 0 | |||
fprintf(stderr, "do_separation_line column_idx = %d %d\n", page_layout->do_sep aration_line, column_idx); | fprintf(stderr, "do_separation_line column_idx = %d %d\n", page_layout->do_sep aration_line, column_idx); | |||
#endif | #endif | |||
if (!page_layout->do_separation_line) | if (!page_layout->do_separation_line) | |||
return; | return; | |||
skipping to change at line 1084 | skipping to change at line 1252 | |||
if (column_idx == 1) | if (column_idx == 1) | |||
total_gutter = 1.0 * page_layout->gutter_width /2; | total_gutter = 1.0 * page_layout->gutter_width /2; | |||
else | else | |||
total_gutter = (column_idx - 0.5) * page_layout->gutter_width; | total_gutter = (column_idx - 0.5) * page_layout->gutter_width; | |||
x_pos = page_layout->left_margin | x_pos = page_layout->left_margin | |||
+ page_layout->column_width * column_idx | + page_layout->column_width * column_idx | |||
+ total_gutter; | + total_gutter; | |||
y_top = page_layout->page_height - page_layout->top_margin - page_layout->head | y_top = page_layout->top_margin + page_layout->header_height + page_layout->he | |||
er_height - page_layout->header_sep / 2; | ader_sep / 2; | |||
y_bot = page_layout->bottom_margin - page_layout->footer_height; | y_bot = page_layout->page_height - page_layout->bottom_margin - page_layout->f | |||
ooter_height; | ||||
g_string_append_printf(ps_pages_string, | cairo_move_to(cr,x_pos, y_top); | |||
"%f %f moveto %f %f lineto 0 setlinewidth stroke\n", | cairo_line_to(cr,x_pos, y_bot); | |||
x_pos, y_top, | cairo_set_line_width(cr, 0.1); | |||
x_pos, y_bot); | cairo_stroke(cr); | |||
} | } | |||
void eject_page(FILE *OUT) | void eject_page(cairo_t *cr) | |||
{ | { | |||
g_string_append_printf(ps_pages_string, | cairo_show_page(cr); | |||
"paps_eop\n" | ||||
"showpage\n"); | ||||
} | } | |||
void start_page(FILE *OUT, | void start_page(cairo_surface_t *surface, | |||
int page_idx) | cairo_t *cr, | |||
page_layout_t *page_layout) | ||||
{ | { | |||
g_string_append_printf(ps_pages_string, | cairo_identity_matrix(cr); | |||
"%%%%Page: %d %d\n" | ||||
"paps_bop\n", | if (output_format == FORMAT_POSTSCRIPT) | |||
page_idx, page_idx); | cairo_ps_surface_dsc_begin_page_setup (surface); | |||
if (page_layout->do_landscape) | ||||
{ | ||||
if (output_format == FORMAT_POSTSCRIPT) | ||||
{ | ||||
cairo_ps_surface_dsc_comment (surface, "%%PageOrientation: Landscape") | ||||
; | ||||
cairo_translate(cr, 0, page_layout->page_width); | ||||
cairo_rotate(cr, 3*M_PI/2); | ||||
} | ||||
} | ||||
else | ||||
{ | ||||
if (output_format == FORMAT_POSTSCRIPT) | ||||
cairo_ps_surface_dsc_comment (surface, "%%PageOrientation: Portrait"); | ||||
} | ||||
} | } | |||
void | void | |||
draw_line_to_page(FILE *OUT, | draw_line_to_page(cairo_t *cr, | |||
int column_idx, | int column_idx, | |||
int column_pos, | int column_pos, | |||
page_layout_t *page_layout, | page_layout_t *page_layout, | |||
PangoLayoutLine *line) | PangoLayoutLine *line, | |||
gboolean draw_wrap_character) | ||||
{ | { | |||
/* Assume square aspect ratio for now */ | /* Assume square aspect ratio for now */ | |||
double y_pos = page_layout->page_height | double y_pos = page_layout->top_margin | |||
- page_layout->top_margin | + page_layout->header_sep | |||
- page_layout->header_sep | + column_pos / PANGO_SCALE; | |||
- column_pos / PANGO_SCALE * page_layout->pixel_to_pt; | ||||
double x_pos = page_layout->left_margin | double x_pos = page_layout->left_margin | |||
+ column_idx * (page_layout->column_width | + column_idx * (page_layout->column_width | |||
+ page_layout->gutter_width); | + page_layout->gutter_width); | |||
gchar *ps_layout; | ||||
PangoRectangle ink_rect, logical_rect; | PangoRectangle ink_rect, logical_rect; | |||
/* Do RTL column layout for RTL direction */ | /* Do RTL column layout for RTL direction */ | |||
if (page_layout->pango_dir == PANGO_DIRECTION_RTL) | if (page_layout->pango_dir == PANGO_DIRECTION_RTL) | |||
{ | { | |||
x_pos = page_layout->left_margin | x_pos = page_layout->left_margin | |||
+ (page_layout->num_columns-1-column_idx) | + (page_layout->num_columns-1-column_idx) | |||
* (page_layout->column_width + page_layout->gutter_width); | * (page_layout->column_width + page_layout->gutter_width); | |||
} | } | |||
pango_layout_line_get_extents(line, | pango_layout_line_get_extents(line, | |||
&ink_rect, | &ink_rect, | |||
&logical_rect); | &logical_rect); | |||
if (page_layout->pango_dir == PANGO_DIRECTION_RTL) { | if (page_layout->pango_dir == PANGO_DIRECTION_RTL) { | |||
x_pos += page_layout->column_width - logical_rect.width / (page_layout->p | x_pos += page_layout->column_width - logical_rect.width / PANGO_SCALE; | |||
t_to_pixel * PANGO_SCALE); | } | |||
cairo_move_to(cr, x_pos, y_pos); | ||||
pango_cairo_show_layout_line(cr, line); | ||||
if (draw_wrap_character) | ||||
{ | ||||
cairo_set_font_face(cr, paps_glyph_face); | ||||
cairo_set_font_size(cr, glyph_font_size); | ||||
if (page_layout->pango_dir == PANGO_DIRECTION_LTR) | ||||
{ | ||||
cairo_move_to(cr, x_pos + page_layout->column_width, y_pos); | ||||
cairo_show_text(cr, "R"); | ||||
} | ||||
else | ||||
{ | ||||
double left_margin = page_layout->left_margin | ||||
+ (page_layout->num_columns-1-column_idx) | ||||
* (page_layout->column_width + page_layout->gutter_width); | ||||
cairo_move_to(cr, left_margin, y_pos); | ||||
cairo_show_text(cr, "L"); | ||||
} | ||||
} | ||||
} | ||||
/* | ||||
* Provide date string from current locale converted to UTF-8. | ||||
*/ | ||||
char * | ||||
get_date(char *date, int maxlen) | ||||
{ | ||||
time_t t; | ||||
GIConv cvh = NULL; | ||||
GString *inbuf; | ||||
char *ib, *ob, obuffer[BUFSIZE * 6], *bp; | ||||
gsize iblen, oblen; | ||||
static char *date_utf8 = NULL; | ||||
if (date_utf8 == NULL) { | ||||
t = time(NULL); | ||||
strftime(date, maxlen, "%c", localtime(&t)); | ||||
cvh = g_iconv_open("UTF-8", get_encoding()); | ||||
if (cvh == (GIConv)-1) { | ||||
fprintf(stderr, _("%s: Invalid encoding: %s\n"), g_get_prgname(), get_enco | ||||
ding()); | ||||
exit(1); | ||||
} | ||||
inbuf = g_string_new(NULL); | ||||
ib = bp = date; | ||||
iblen = strlen(ib); | ||||
ob = bp = obuffer; | ||||
oblen = BUFSIZE * 6 - 1; | ||||
if (g_iconv(cvh, &ib, &iblen, &ob, &oblen) == (gsize)-1) { | ||||
fprintf(stderr, _("%1$s: Error while converting date string from '%2$s' to | ||||
UTF-8.\n"), | ||||
g_get_prgname(), get_encoding()); | ||||
/* Return the unconverted string. */ | ||||
g_string_free(inbuf, FALSE); | ||||
g_iconv_close(cvh); | ||||
return date; | ||||
} | ||||
obuffer[BUFSIZE * 6 - 1 - oblen] = 0; | ||||
g_string_append(inbuf, bp); | ||||
date_utf8 = inbuf->str; | ||||
g_string_free(inbuf, FALSE); | ||||
g_iconv_close(cvh); | ||||
} | } | |||
paps_set_scale(paps, page_layout->scale_x, page_layout->scale_y); | return date_utf8; | |||
ps_layout = paps_layout_line_to_postscript_strdup(paps, | ||||
x_pos, y_pos, | ||||
line); | ||||
g_string_append(ps_pages_string, | ||||
ps_layout); | ||||
g_free(ps_layout); | ||||
} | } | |||
int | int | |||
draw_page_header_line_to_page(FILE *OUT, | draw_page_header_line_to_page(cairo_t *cr, | |||
gboolean is_footer, | gboolean is_footer, | |||
page_layout_t *page_layout, | page_layout_t *page_layout, | |||
PangoContext *ctx, | PangoContext *ctx, | |||
int page) | int page) | |||
{ | { | |||
PangoLayout *layout = pango_layout_new(ctx); | PangoLayout *layout = pango_layout_new(ctx); | |||
PangoLayoutLine *line; | PangoLayoutLine *line; | |||
PangoRectangle ink_rect, logical_rect; | PangoRectangle ink_rect, logical_rect, pagenum_rect; | |||
/* Assume square aspect ratio for now */ | /* Assume square aspect ratio for now */ | |||
double x_pos, y_pos; | double x_pos, y_pos; | |||
gchar *ps_layout, *header, date[256]; | gchar *header, date[256]; | |||
time_t t; | ||||
struct tm tm; | ||||
int height; | int height; | |||
gdouble line_pos; | gdouble line_pos; | |||
t = time(NULL); | /* Reset gravity?? */ | |||
tm = *localtime(&t); | #if 0 | |||
strftime(date, 255, "%c", &tm); | ||||
header = g_strdup_printf("<span font_desc=\"%s\">%s</span>\n" | header = g_strdup_printf("<span font_desc=\"%s\">%s</span>\n" | |||
"<span font_desc=\"%s\">%s</span>\n" | "<span font_desc=\"%s\">%s</span>\n" | |||
"<span font_desc=\"%s\">Page %d</span>", | "<span font_desc=\"%s\">%d</span>", | |||
page_layout->header_font_desc, | ||||
page_layout->title, | ||||
page_layout->header_font_desc, | page_layout->header_font_desc, | |||
date, | get_date(date, 255), | |||
page_layout->header_font_desc, | page_layout->header_font_desc, | |||
page_layout->filename, | page); | |||
#endif | ||||
header = g_strdup_printf("<span font_desc=\"%s\">%d</span>\n", | ||||
page_layout->header_font_desc, | page_layout->header_font_desc, | |||
page); | page); | |||
pango_layout_set_markup(layout, header, -1); | pango_layout_set_markup(layout, header, -1); | |||
g_free(header); | g_free(header); | |||
/* output a left edge of header/footer */ | /* output a left edge of header/footer */ | |||
line = pango_layout_get_line(layout, 0); | line = pango_layout_get_line(layout, 0); | |||
pango_layout_line_get_extents(line, | pango_layout_line_get_extents(line, | |||
&ink_rect, | &ink_rect, | |||
&logical_rect); | &logical_rect); | |||
x_pos = page_layout->left_margin; | x_pos = page_layout->left_margin + (page_layout->page_width-page_layout->left_ | |||
height = logical_rect.height / PANGO_SCALE * page_layout->pixel_to_pt/3.0; | margin-page_layout->right_margin)*0.5 - 0.5*logical_rect.width/PANGO_SCALE; | |||
height = logical_rect.height / PANGO_SCALE /3.0; | ||||
/* The header is placed right after the margin */ | /* The header is placed right after the margin */ | |||
if (is_footer) | if (is_footer) | |||
{ | { | |||
y_pos = page_layout->bottom_margin; | y_pos = page_layout->page_height - page_layout->bottom_margin*0.5; | |||
page_layout->footer_height = height; | page_layout->footer_height = height; | |||
} | } | |||
else | else | |||
{ | { | |||
y_pos = page_layout->page_height - page_layout->top_margin - height; | y_pos = page_layout->top_margin + height; | |||
page_layout->header_height = height; | page_layout->header_height = height; | |||
} | } | |||
paps_set_scale(paps, page_layout->scale_x, page_layout->scale_y); | ||||
ps_layout = paps_layout_line_to_postscript_strdup(paps, | ||||
x_pos, y_pos, | ||||
line); | ||||
g_string_append(ps_pages_string, | ||||
ps_layout); | ||||
g_free(ps_layout); | ||||
/* output a center of header/footer */ | cairo_move_to(cr, x_pos,y_pos); | |||
line = pango_layout_get_line(layout, 1); | pango_cairo_show_layout_line(cr,line); | |||
/* output a right edge of header/footer */ | ||||
line = pango_layout_get_line(layout, 2); | ||||
pango_layout_line_get_extents(line, | pango_layout_line_get_extents(line, | |||
&ink_rect, | &ink_rect, | |||
&logical_rect); | &logical_rect); | |||
x_pos = (page_layout->page_width - (logical_rect.width / PANGO_SCALE * page_la | pagenum_rect = logical_rect; | |||
yout->pixel_to_pt)) / 2; | x_pos = page_layout->page_width - page_layout->right_margin - (logical_rect.wi | |||
paps_set_scale(paps, page_layout->scale_x, page_layout->scale_y); | dth / PANGO_SCALE ); | |||
ps_layout = paps_layout_line_to_postscript_strdup(paps, | cairo_move_to(cr, x_pos,y_pos); | |||
x_pos, y_pos, | pango_cairo_show_layout_line(cr,line); | |||
line); | ||||
g_string_append(ps_pages_string, | ||||
ps_layout); | ||||
g_free(ps_layout); | ||||
/* output a right edge of header/footer */ | /* output a "center" of header/footer */ | |||
line = pango_layout_get_line(layout, 2); | line = pango_layout_get_line(layout, 1); | |||
pango_layout_line_get_extents(line, | pango_layout_line_get_extents(line, | |||
&ink_rect, | &ink_rect, | |||
&logical_rect); | &logical_rect); | |||
x_pos = page_layout->page_width - page_layout->right_margin - (logical_rect.wi | x_pos = page_layout->page_width - page_layout->right_margin - | |||
dth / PANGO_SCALE * page_layout->pixel_to_pt); | ((logical_rect.width + pagenum_rect.width) / PANGO_SCALE + page_layout->gu | |||
paps_set_scale(paps, page_layout->scale_x, page_layout->scale_y); | tter_width); | |||
ps_layout = paps_layout_line_to_postscript_strdup(paps, | cairo_move_to(cr, x_pos,y_pos); | |||
x_pos, y_pos, | pango_cairo_show_layout_line(cr,line); | |||
line); | ||||
g_string_append(ps_pages_string, | ||||
ps_layout); | ||||
g_free(ps_layout); | ||||
g_object_unref(layout); | g_object_unref(layout); | |||
/* header separator */ | /* header separator */ | |||
line_pos = page_layout->page_height - page_layout->top_margin - page_layout->h | #if 0 | |||
eader_height - page_layout->header_sep / 2; | line_pos = page_layout->top_margin + page_layout->header_height + page_layout- | |||
g_string_append_printf(ps_pages_string, | >header_sep / 2; | |||
"%d %f moveto %d %f lineto 0 setlinewidth stroke\n", | cairo_move_to(cr, page_layout->left_margin, line_pos); | |||
page_layout->left_margin, line_pos, | cairo_line_to(cr,page_layout->page_width - page_layout->right_margin, line_pos | |||
page_layout->page_width - page_layout->right_margin, li | ); | |||
ne_pos); | cairo_set_line_width(cr,0.1); // TBD | |||
cairo_stroke(cr); | ||||
#endif | ||||
return logical_rect.height; | return logical_rect.height; | |||
} | } | |||
End of changes. 159 change blocks. | ||||
617 lines changed or deleted | 863 lines changed or added |