/*=============================================================================
GNU UnRTF, a command-line program to convert RTF documents to other formats.
Copyright (C) 2000,2001,2004 by Zachary Smith
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
The maintainer is reachable by electronic mail at daved@physiol.usyd.edu.au
=============================================================================*/
/*----------------------------------------------------------------------
* Module name: output
* Author name: Zachary Smith
* Create date: 18 Sep 01
* Purpose: Generalized output module
*----------------------------------------------------------------------
* Changes:
* 22 Sep 01, tuorfa@yahoo.com: addition of functions to change font size
* 22 Sep 01, tuorfa@yahoo.com: added function-level comment blocks
* 08 Oct 03, daved@physiol.usyd.edu.au: added stdlib.h for linux
* 25 Sep 04, st001906@hrz1.hrz.tu-darmstadt.de: added stdlib.h for djgpp
* 29 Mar 05, daved@physiol.usyd.edu.au: changes requested by ZT Smith
* 06 Jan 06, marcossamaral@terra.com.br: changes in STDOUT
* 16 Dec 07, daved@physiol.usyd.edu.au: updated to GPL v3
* 17 Dec 07, daved@physiol.usyd.edu.au: added support for --noremap from
* David Santinoli
* 09 Nov 08, arkadiusz.firus@gmail.com: use iconv
* 07 Oct 11, jf@dockes.org: replaced op_translate_doublet with
* op_translate_buffer and other major changes
*--------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <arpa/inet.h>
#include "malloc.h"
#include "defs.h"
#include "error.h"
#include "output.h"
#include "main.h"
#include "convert.h"
#include "attr.h"
#include "util.h"
/*========================================================================
* Name: op_create
* Purpose: Creates a blank output personality.
* Args: None.
* Returns: Output personality struct.
*=======================================================================*/
OutputPersonality*
op_create ()
{
OutputPersonality* new_op;
new_op = (OutputPersonality*) my_malloc (sizeof(OutputPersonality));
if (!new_op)
error_handler ("cannot allocate output personality");
memset ((void*) new_op, 0, sizeof (OutputPersonality));
return new_op;
}
/*========================================================================
* Name: op_free
* Purpose: Deallocates an output personality, but none of the strings
* it points to since they are usually constants.
* Args: OutputPersonality.
* Returns: None.
*=======================================================================*/
void
op_free (OutputPersonality *op)
{
CHECK_PARAM_NOT_NULL(op);
my_free ((void*) op);
}
/*========================================================================
* Name: op_translate_char
* Purpose: Native encoding value->Unicode number as int
* Then transform as specified by op (ie, entity, &#xxx;...)
* Currently this is only called with values < 255 (single byte chars or
* computation of Symbol code point in fields).
* We cache a local iconv descriptor for efficiency.
* Args: OutputPersonality, encoding, character.
* Returns: String.
*=======================================================================*/
char *
op_translate_char (OutputPersonality *op, char *encoding, int ch)
{
static my_iconv_t m_desc = MY_ICONV_T_CLEAR;
static char *m_encoding = NULL;
char *result = NULL;
static char output_buffer[2]={ 0, 0 };
CHECK_PARAM_NOT_NULL(op);
if (ch < 0 || ch > 255) {
fprintf(stderr, "op_translate_char called with bad char 0x%x\n",
ch);
return NULL;
}
if (no_remap_mode == TRUE && ch < 256) {
/* Kept for historical reason, makes no sense in a multi-encoding
document */
output_buffer[0] = ch;
result = output_buffer;
} else {
if (!m_encoding || strcasecmp(encoding, m_encoding)) {
// Using UTF-32BE to avoid BOM
if (my_iconv_is_valid(m_desc))
my_iconv_close(m_desc);
m_desc = my_iconv_open("UTF-32BE", encoding);
if (!my_iconv_is_valid(m_desc)) {
return NULL;
}
if (m_encoding)
my_free(m_encoding);
m_encoding = my_strdup(encoding);
}
size_t inbytes = 1, outbytes = 4;
char inbuf[1];
uint32_t outbuf;
char *icp = inbuf, *ocp = (char*)&outbuf;
int unicode;
inbuf[0] = ch;
if (my_iconv(m_desc, &icp, &inbytes, &ocp, &outbytes) == -1)
{
/* fprintf(stderr, "unrtf: iconv error %s->UTF-32BE for 0x%x\n",
encoding, ch);*/
return NULL;
}
/* Conversion from UTF-32BE string to int */
unicode = ntohl(outbuf);
#if 0
if (unicode != ch)
fprintf(stderr, "op_translate: 0x%x -> unicode 0x%x\n",
(unsigned)ch, (unsigned)unicode);
#endif
/* Output Unicode value as decided by output personality */
result = get_alias(op, unicode); /* Try for ie, entity */
if (result == NULL) {
if (ch > 127 && op->unisymbol_print) {
/* TODO: memory leak here. Impossible to fix easily as we can
return either a static or allocated buffer */
result = assemble_string(op->unisymbol_print, unicode);
} else {
/* Single byte unicode char needs no translation ever?*/
output_buffer[0] = ch;
result = output_buffer;
}
}
}
return result;
}
/*========================================================================
* Name: op_translate_buffer
* Purpose: Translate buffer built out of \' commands in convert.c
* The data comes in as UTF-32BE, which we change to unicode positions
* (just the byte order), then transform according to outputpersonality
* TOBEDONE: have a way to specify direct UTF-8 output for a personality
* (then produce utf-8 in convert.c and do nothing special here.
*
* Args: OutputPersonality, encoding, character.
* Returns: malloced string.
*=======================================================================*/
char *
op_translate_buffer(OutputPersonality *op, char *buffer, int bytescnt)
{
int wordcnt = bytescnt / 4;
int i;
int opos = 0;
char *out = 0;
int outsz;
if ((out = (char *)allocmem(out, 1, &outsz, 1000, 100000)) == NULL) {
fprintf(stderr, "unrtf: out of memory\n");
return NULL;
}
for (i = 0; i < wordcnt; i++) {
char tbuf[2];
char *result;
int incsz;
int unicode = ntohl(*(int*)(buffer + 4 * i));
result = get_alias(op, unicode); /* Try for ie, entity */
if (result != NULL) {
incsz = strlen(result);
} else {
if (unicode > 127 && op->unisymbol_print) {
/* TODO: fix memory leak */
result = assemble_string(op->unisymbol_print, unicode);
incsz = strlen(result);
} else {
/* Single byte unicode char needs no translation ever?*/
tbuf[0] = unicode;
tbuf[1] = 0;
result = tbuf;
incsz = 1;
}
}
if (opos + incsz + 1 >= outsz) {
if ((out = (char *)allocmem(out, 1, &outsz, 1000, 100000))) {
fprintf(stderr, "unrtf: out of memory\n");
return NULL;
}
}
strcpy(out + opos, result);
opos += incsz;
}
return out;
}
/*========================================================================
* Name: op_begin_std_fontsize
* Purpose: Prints whatever is necessary to perform a change in the
* current font size.
* Args: OutputPersonality, desired size.
* Returns: None.
*=======================================================================*/
void
op_begin_std_fontsize (OutputPersonality *op, int size)
{
int found_std_expr = FALSE;
CHECK_PARAM_NOT_NULL(op);
/* Look for an exact match with a standard point size.
*/
switch (size) {
case 8:
if (op->fontsize8_begin) {
if (safe_printf(0, op->fontsize8_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize8_begin");
found_std_expr = TRUE;
}
break;
case 10:
if (op->fontsize10_begin) {
if (safe_printf(0, op->fontsize10_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize10_begin");
found_std_expr = TRUE;
}
break;
case 12:
if (op->fontsize12_begin) {
if (safe_printf(0, op->fontsize12_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize12_begin");
found_std_expr = TRUE;
}
break;
case 14:
if (op->fontsize14_begin) {
if (safe_printf(0, op->fontsize14_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize14_begin");
found_std_expr = TRUE;
}
break;
case 18:
if (op->fontsize18_begin) {
if (safe_printf(0, op->fontsize18_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize18_begin");
found_std_expr = TRUE;
}
break;
case 24:
if (op->fontsize24_begin) {
if (safe_printf(0, op->fontsize24_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize24_begin");
found_std_expr = TRUE;
}
break;
case 36:
if (op->fontsize36_begin) {
if (safe_printf(0, op->fontsize36_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize36_begin");
found_std_expr = TRUE;
}
break;
case 48:
if (op->fontsize48_begin) {
if (safe_printf(0, op->fontsize48_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize48_begin");
found_std_expr = TRUE;
}
break;
}
/* If no exact match, try to write out a change to the
* exact point size.
*/
if (!found_std_expr) {
if (op->fontsize_begin) {
char expr[16];
sprintf (expr, "%d", size);
if (safe_printf (1, op->fontsize_begin, expr)) fprintf(stderr, TOO_MANY_ARGS, "fontsize_begin");
} else {
/* If we cannot write out a change for the exact
* point size, we must approximate to a standard
* size.
*/
if (size<9 && op->fontsize8_begin) {
if (safe_printf(0, op->fontsize8_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize8_begin");
} else
if (size<11 && op->fontsize10_begin) {
if (safe_printf(0, op->fontsize10_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize10_begin");
} else
if (size<13 && op->fontsize12_begin) {
if (safe_printf(0, op->fontsize12_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize12_begin");
} else
if (size<16 && op->fontsize14_begin) {
if (safe_printf(0, op->fontsize14_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize14_begin");
} else
if (size<21 && op->fontsize18_begin) {
if (safe_printf(0, op->fontsize18_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize18_begin");
} else
if (size<30 && op->fontsize24_begin) {
if (safe_printf(0, op->fontsize24_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize24_begin");
} else
if (size<42 && op->fontsize36_begin) {
if (safe_printf(0, op->fontsize36_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize36_begin");
} else
if (size>40 && op->fontsize48_begin) {
if (safe_printf(0, op->fontsize48_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize48_begin");
} else
/* If we can't even produce a good approximation,
* just try to get a font size near 12 point.
*/
if (op->fontsize12_begin)
if (safe_printf(0, op->fontsize12_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize12_begin");
else
if (op->fontsize14_begin)
if (safe_printf(0, op->fontsize14_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize14_begin");
else
if (op->fontsize10_begin)
if (safe_printf(0, op->fontsize10_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize10_begin");
else
if (op->fontsize18_begin)
if (safe_printf(0, op->fontsize18_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize18_begin");
else
if (op->fontsize8_begin)
if (safe_printf(0, op->fontsize8_begin)) fprintf(stderr, TOO_MANY_ARGS, "fontsize8_begin");
else
error_handler ("output personality lacks sufficient font size change capability");
}
}
}
/*========================================================================
* Name: op_end_std_fontsize
* Purpose: Prints whatever is necessary to perform a change in the
* current font size.
* Args: OutputPersonality, desired size.
* Returns: None.
*=======================================================================*/
void
op_end_std_fontsize (OutputPersonality *op, int size)
{
int found_std_expr = FALSE;
CHECK_PARAM_NOT_NULL(op);
/* Look for an exact match with a standard point size.
*/
switch (size) {
case 8:
if (op->fontsize8_end) {
if (safe_printf(0, op->fontsize8_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize8_end");
found_std_expr = TRUE;
}
break;
case 10:
if (op->fontsize10_end) {
if (safe_printf(0, op->fontsize10_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize10_end");
found_std_expr = TRUE;
}
break;
case 12:
if (op->fontsize12_end) {
if (safe_printf(0, op->fontsize12_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize12_end");
found_std_expr = TRUE;
}
break;
case 14:
if (op->fontsize14_end) {
if (safe_printf(0, op->fontsize14_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize14_end");
found_std_expr = TRUE;
}
break;
case 18:
if (op->fontsize18_end) {
if (safe_printf(0, op->fontsize18_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize18_end");
found_std_expr = TRUE;
}
break;
case 24:
if (op->fontsize24_end) {
if (safe_printf(0, op->fontsize24_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize24_end");
found_std_expr = TRUE;
}
break;
case 36:
if (op->fontsize36_end) {
if (safe_printf(0, op->fontsize36_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize36_end");
found_std_expr = TRUE;
}
break;
case 48:
if (op->fontsize48_end) {
if (safe_printf(0, op->fontsize48_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize48_end");
found_std_expr = TRUE;
}
break;
}
/* If no exact match, try to write out a change to the
* exact point size.
*/
if (!found_std_expr) {
if (op->fontsize_end) {
char expr[16];
sprintf (expr, "%d", size);
if (safe_printf(1, op->fontsize_end, expr)) fprintf(stderr, TOO_MANY_ARGS, "fontsize_end");
} else {
/* If we cannot write out a change for the exact
* point size, we must approximate to a standard
* size.
*/
if (size<9 && op->fontsize8_end) {
if (safe_printf(0, op->fontsize8_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize8_end");
} else
if (size<11 && op->fontsize10_end) {
if (safe_printf(0, op->fontsize10_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize10_end");
} else
if (size<13 && op->fontsize12_end) {
if (safe_printf(0, op->fontsize12_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize12_end");
} else
if (size<16 && op->fontsize14_end) {
if (safe_printf(0, op->fontsize14_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize14_end");
} else
if (size<21 && op->fontsize18_end) {
if (safe_printf(0, op->fontsize18_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize18_end");
} else
if (size<30 && op->fontsize24_end) {
if (safe_printf(0, op->fontsize24_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize24_end");
} else
if (size<42 && op->fontsize36_end) {
if (safe_printf(0, op->fontsize36_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize36_end");
} else
if (size>40 && op->fontsize48_end) {
if (safe_printf(0, op->fontsize48_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize48_end");
} else
/* If we can't even produce a good approximation,
* just try to get a font size near 12 point.
*/
if (op->fontsize12_end)
if (safe_printf(0, op->fontsize12_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize12_end");
else
if (op->fontsize14_end)
if (safe_printf(0, op->fontsize14_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize14_end");
else
if (op->fontsize10_end)
if (safe_printf(0, op->fontsize10_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize10_end");
else
if (op->fontsize18_end)
if (safe_printf(0, op->fontsize18_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize18_end");
else
if (op->fontsize8_end)
if (safe_printf(0, op->fontsize8_end)) fprintf(stderr, TOO_MANY_ARGS, "fontsize8_end");
else
error_handler ("output personality lacks sufficient font size change capability");
}
}
}
#if 1 /* AK3 - AF */
/*========================================================================
* Name: add_alias
* Purpose: Adds alias (text) for a char number nr.
* Args: OutputPersonality, char's number, alias.
* Returns: None.
*=======================================================================*/
void
add_alias(OutputPersonality *op, int nr, char *text)
{
op->aliases = (Aliases *)add_to_collection((Collection *)op->aliases, nr, text);
}
/*========================================================================
* Name: get_alias
* Purpose: Search for alias for character number nr.
* Args: OutputPersonality, char's number.
* Returns: Text alias or NULL if found nothing.
*=======================================================================*/
char *
get_alias(OutputPersonality *op, int nr)
{
return get_from_collection((Collection *)op->aliases, nr);
}
#endif