/* $Header: /home/cvsroot/dvipdfmx/src/spc_html.c,v 1.6 2007/01/19 11:00:48 chofchof Exp $
This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
Copyright (C) 2002 by Jin-Hwan Cho and Shunsaku Hirata,
the dvipdfmx project team
Copyright (C) 1998, 1999 by Mark A. Wicks
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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "system.h"
#include "mem.h"
#include "error.h"
#include "dpxutil.h"
#include "pdfdraw.h"
#include "pdfdev.h"
#include "pdfximage.h"
#include "pdfdoc.h"
#include "specials.h"
#include "spc_util.h"
#include "spc_html.h"
#define ENABLE_HTML_IMG_SUPPORT 1
#define ENABLE_HTML_SVG_TRANSFORM 1
#define ENABLE_HTML_SVG_OPACITY 1
/* _FIXME_
* Please rewrite this or remove html special support
*/
#define ANCHOR_TYPE_HREF 0
#define ANCHOR_TYPE_NAME 1
struct spc_html_
{
struct {
long extensions;
} opts;
pdf_obj *link_dict;
char *baseurl;
int pending_type;
};
static struct spc_html_ _html_state = {
{ 0 },
NULL, NULL, -1
};
#ifdef ENABLE_HTML_SVG_TRANSFORM
static int cvt_a_to_tmatrix (pdf_tmatrix *M, const char *ptr, char **nextptr);
#endif /* ENABLE_HTML_SVG_TRANSFORM */
#define \
downcasify(s) \
if ((s)) { \
char *_p = (char *) (s); \
while (*(_p) != 0) { \
if (*(_p) >= 'A' && *(_p) <= 'Z') { \
*(_p) = (*(_p) - 'A') + 'a'; \
} \
_p++; \
} \
}
static int
parse_key_val (char **pp, char *endptr, char **kp, char **vp)
{
char *q, *p, *k, *v;
int n, error = 0;
for (p = *pp ; p < endptr && isspace(*p); p++);
#if 0
while (!error && p < endptr &&
((*p >= 'a' && *p <= 'z') ||
(*p >= 'A' && *p <= 'Z'))
) {
#endif
k = v = NULL;
for (q = p, n = 0;
p < endptr &&
((*p >= 'a' && *p <= 'z') ||
(*p >= 'A' && *p <= 'Z') ||
(*p >= '0' && *p <= '9') ||
*p == '-' || *p == ':'
); n++, p++);
if (n == 0) {
#if 0
break;
#else
*kp = *vp = NULL;
return -1;
#endif
}
k = NEW(n + 1, char);
memcpy(k, q, n); k[n] = '\0';
if (p + 2 >= endptr || p[0] != '=' || (p[1] != '\"' && p[1] != '\'')) {
RELEASE(k); k = NULL;
*pp = p;
error = -1;
} else {
char qchr = p[1];
p += 2; /* skip '="' */
for (q = p, n = 0; p < endptr && *p != qchr; p++, n++);
if (p == endptr || *p != qchr)
error = -1;
else {
v = NEW(n + 1, char);
memcpy(v, q, n); v[n] = '\0';
#if 0
pdf_add_dict(t->attr,
pdf_new_name(k),
pdf_new_string(v, n));
RELEASE(v);
#endif
p++;
}
}
#if 0
RELEASE(k);
if (!error)
for ( ; p < endptr && isspace(*p); p++);
}
#endif
*kp = k; *vp = v; *pp = p;
return error;
}
#define HTML_TAG_NAME_MAX 127
#define HTML_TAG_TYPE_EMPTY 1
#define HTML_TAG_TYPE_OPEN 1
#define HTML_TAG_TYPE_CLOSE 2
static int
read_html_tag (char *name, pdf_obj *attr, int *type, char **pp, char *endptr)
{
char *p = *pp;
int n = 0, error = 0;
for ( ; p < endptr && isspace(*p); p++);
if (p >= endptr || *p != '<')
return -1;
*type = HTML_TAG_TYPE_OPEN;
for (++p; p < endptr && isspace(*p); p++);
if (p < endptr && *p == '/') {
*type = HTML_TAG_TYPE_CLOSE;
for (++p; p < endptr && isspace(*p); p++);
}
#define ISDELIM(c) ((c) == '>' || (c) == '/' || isspace(c))
for (n = 0; p < endptr && n < HTML_TAG_NAME_MAX && !ISDELIM(*p); n++, p++) {
name[n] = *p;
}
name[n] = '\0';
if (n == 0 || p == endptr || !ISDELIM(*p)) {
*pp = p;
return -1;
}
for ( ; p < endptr && isspace(*p); p++);
while (p < endptr && !error && *p != '/' && *p != '>') {
char *kp = NULL, *vp = NULL;
error = parse_key_val(&p, endptr, &kp, &vp);
if (!error) {
downcasify(kp);
pdf_add_dict(attr,
pdf_new_name(kp),
pdf_new_string(vp, strlen(vp) + 1)); /* include trailing NULL here!!! */
RELEASE(kp);
RELEASE(vp);
}
for ( ; p < endptr && isspace(*p); p++);
}
if (error) {
*pp = p;
return error;
}
if (p < endptr && *p == '/') {
*type = HTML_TAG_TYPE_EMPTY;
for (++p; p < endptr && isspace(*p); p++);
}
if (p == endptr || *p != '>') {
*pp = p;
return -1;
}
p++;
downcasify(name);
*pp = p;
return 0;
}
static int
spc_handler_html__init (struct spc_env *spe, struct spc_arg *ap, void *dp)
{
struct spc_html_ *sd = dp;
sd->link_dict = NULL;
sd->baseurl = NULL;
sd->pending_type = -1;
return 0;
}
static int
spc_handler_html__clean (struct spc_env *spe, struct spc_arg *ap, void *dp)
{
struct spc_html_ *sd = dp;
if (sd->baseurl)
RELEASE(sd->baseurl);
if (sd->pending_type >= 0 || sd->link_dict)
spc_warn(spe, "Unclosed html anchor found.");
if (sd->link_dict)
pdf_release_obj(sd->link_dict);
sd->pending_type = -1;
sd->baseurl = NULL;
sd->link_dict = NULL;
return 0;
}
static int
spc_handler_html__bophook (struct spc_env *spe, struct spc_arg *ap, void *dp)
{
struct spc_html_ *sd = dp;
if (sd->pending_type >= 0) {
spc_warn(spe, "...html anchor continues from previous page processed...");
}
return 0;
}
static int
spc_handler_html__eophook (struct spc_env *spe, struct spc_arg *ap, void *dp)
{
struct spc_html_ *sd = dp;
if (sd->pending_type >= 0) {
spc_warn(spe, "Unclosed html anchor at end-of-page!");
}
return 0;
}
static char *
fqurl (const char *baseurl, const char *name)
{
char *q;
int len = 0;
len = strlen(name);
if (baseurl)
len += strlen(baseurl) + 1; /* we may want to add '/' */
q = NEW(len + 1, char);
*q = '\0';
if (baseurl && baseurl[0]) {
char *p;
strcpy(q, baseurl);
p = q + strlen(q) - 1;
if (*p == '/')
*p = '\0';
if (name[0] && name[0] != '/')
strcat(q, "/");
}
strcat(q, name);
return q;
}
static int
html_open_link (struct spc_env *spe, const char *name, struct spc_html_ *sd)
{
pdf_obj *color;
char *url;
ASSERT( name );
ASSERT( sd->link_dict == NULL ); /* Should be checked somewhere else */
sd->link_dict = pdf_new_dict();
pdf_add_dict(sd->link_dict,
pdf_new_name("Type"), pdf_new_name ("Annot"));
pdf_add_dict(sd->link_dict,
pdf_new_name("Subtype"), pdf_new_name ("Link"));
color = pdf_new_array ();
pdf_add_array(color, pdf_new_number(0.0));
pdf_add_array(color, pdf_new_number(0.0));
pdf_add_array(color, pdf_new_number(1.0));
pdf_add_dict(sd->link_dict, pdf_new_name("C"), color);
url = fqurl(sd->baseurl, name);
if (url[0] == '#') {
/* url++; causes memory leak in RELEASE(url) */
pdf_add_dict(sd->link_dict,
pdf_new_name("Dest"),
pdf_new_string(url+1, strlen(url+1)));
} else { /* Assume this is URL */
pdf_obj *action = pdf_new_dict();
pdf_add_dict(action,
pdf_new_name("Type"),
pdf_new_name("Action"));
pdf_add_dict(action,
pdf_new_name("S"),
pdf_new_name("URI"));
pdf_add_dict(action,
pdf_new_name("URI"),
pdf_new_string(url, strlen(url)));
pdf_add_dict(sd->link_dict,
pdf_new_name("A"),
pdf_link_obj(action));
pdf_release_obj(action);
}
RELEASE(url);
spc_begin_annot(spe, sd->link_dict);
sd->pending_type = ANCHOR_TYPE_HREF;
return 0;
}
static int
html_open_dest (struct spc_env *spe, const char *name, struct spc_html_ *sd)
{
int error;
pdf_obj *array, *page_ref;
pdf_coord cp;
cp.x = spe->x_user; cp.y = spe->y_user;
pdf_dev_transform(&cp, NULL);
page_ref = pdf_doc_this_page_ref();
ASSERT( page_ref ); /* Otherwise must be bug */
array = pdf_new_array();
pdf_add_array(array, page_ref);
pdf_add_array(array, pdf_new_name("XYZ"));
pdf_add_array(array, pdf_new_null());
pdf_add_array(array, pdf_new_number(cp.y + 24.0));
pdf_add_array(array, pdf_new_null());
error = pdf_doc_add_names("Dests",
name, strlen(name),
array);
if (error)
spc_warn(spe, "Failed to add named destination: %s", name);
sd->pending_type = ANCHOR_TYPE_NAME;
return error;
}
#define ANCHOR_STARTED(s) ((s)->pending_type >= 0 || (s)->link_dict)
static int
spc_html__anchor_open (struct spc_env *spe, pdf_obj *attr, struct spc_html_ *sd)
{
pdf_obj *href, *name;
int error = 0;
if (ANCHOR_STARTED(sd)) {
spc_warn(spe, "Nested html anchors found!");
return -1;
}
href = pdf_lookup_dict(attr, "href");
name = pdf_lookup_dict(attr, "name");
if (href && name) {
spc_warn(spe, "Sorry, you can't have both \"href\" and \"name\" in anchor tag...");
error = -1;
} else if (href) {
error = html_open_link(spe, pdf_string_value(href), sd);
} else if (name) { /* name */
error = html_open_dest(spe, pdf_string_value(name), sd);
} else {
spc_warn(spe, "You should have \"href\" or \"name\" in anchor tag!");
error = -1;
}
return error;
}
static int
spc_html__anchor_close (struct spc_env *spe, pdf_obj *attr, struct spc_html_ *sd)
{
int error = 0;
switch (sd->pending_type) {
case ANCHOR_TYPE_HREF:
if (sd->link_dict) {
spc_end_annot(spe);
pdf_release_obj(sd->link_dict);
sd->link_dict = NULL;
sd->pending_type = -1;
} else {
spc_warn(spe, "Closing html anchor (link) without starting!");
error = -1;
}
break;
case ANCHOR_TYPE_NAME:
sd->pending_type = -1;
break;
default:
spc_warn(spe, "No corresponding opening tag for html anchor.");
error = -1;
break;
}
return error;
}
static int
spc_html__base_empty (struct spc_env *spe, pdf_obj *attr, struct spc_html_ *sd)
{
pdf_obj *href;
char *vp;
href = pdf_lookup_dict(attr, "href");
if (!href) {
spc_warn(spe, "\"href\" not found for \"base\" tag!");
return -1;
}
vp = (char *) pdf_string_value(href);
if (sd->baseurl) {
spc_warn(spe, "\"baseurl\" changed: \"%s\" --> \"%s\"", sd->baseurl, vp);
RELEASE(sd->baseurl);
}
sd->baseurl = NEW(strlen(vp) + 1, char);
strcpy(sd->baseurl, vp);
return 0;
}
#ifdef ENABLE_HTML_IMG_SUPPORT
/* This isn't completed.
* Please think about placement of images.
*/
static double
atopt (const char *a)
{
char *q, *p = (char *) a;
double v, u = 1.0;
const char *_ukeys[] = {
#define K_UNIT__PT 0
#define K_UNIT__IN 1
#define K_UNIT__CM 2
#define K_UNIT__MM 3
#define K_UNIT__BP 4
"pt", "in", "cm", "mm", "bp",
#define K_UNIT__PX 5
"px",
NULL
};
int k;
q = parse_float_decimal(&p, p + strlen(p));
if (!q) {
WARN("Invalid length value: %s (%c)", a, *p);
return 0.0;
}
v = atof(q);
RELEASE(q);
q = parse_c_ident(&p, p + strlen(p));
if (q) {
for (k = 0; _ukeys[k] && strcmp(_ukeys[k], q); k++);
switch (k) {
case K_UNIT__PT: u *= 72.0 / 72.27; break;
case K_UNIT__IN: u *= 72.0; break;
case K_UNIT__CM: u *= 72.0 / 2.54 ; break;
case K_UNIT__MM: u *= 72.0 / 25.4 ; break;
case K_UNIT__BP: u *= 1.0 ; break;
case K_UNIT__PX: u *= 1.0 ; break; /* 72dpi */
default:
WARN("Unknown unit of measure: %s", q);
break;
}
RELEASE(q);
}
return v * u;
}
#ifdef ENABLE_HTML_SVG_OPACITY
/* Replicated from spc_tpic */
static pdf_obj *
create_xgstate (double a /* alpha */, int f_ais /* alpha is shape */)
{
pdf_obj *dict;
dict = pdf_new_dict();
pdf_add_dict(dict,
pdf_new_name("Type"),
pdf_new_name("ExtGState"));
if (f_ais) {
pdf_add_dict(dict,
pdf_new_name("AIS"),
pdf_new_boolean(1));
}
pdf_add_dict(dict,
pdf_new_name("ca"),
pdf_new_number(a));
return dict;
}
static int
check_resourcestatus (const char *category, const char *resname)
{
pdf_obj *dict1, *dict2;
dict1 = pdf_doc_current_page_resources();
if (!dict1)
return 0;
dict2 = pdf_lookup_dict(dict1, category);
if (dict2 &&
pdf_obj_typeof(dict2) == PDF_DICT) {
if (pdf_lookup_dict(dict2, resname))
return 1;
}
return 0;
}
#endif /* ENABLE_HTML_SVG_OPACITY */
static int
spc_html__img_empty (struct spc_env *spe, pdf_obj *attr, struct spc_html_ *sd)
{
pdf_obj *src, *obj;
transform_info ti;
int id, error = 0;
#ifdef ENABLE_HTML_SVG_OPACITY
double alpha = 1.0; /* meaning fully opaque */
#endif /* ENABLE_HTML_SVG_OPACITY */
#ifdef ENABLE_HTML_SVG_TRANSFORM
pdf_tmatrix M;
pdf_setmatrix(&M, 1.0, 0.0, 0.0, 1.0, spe->x_user, spe->y_user);
#endif /* ENABLE_HTML_SVG_TRANSFORM */
spc_warn(spe, "html \"img\" tag found (not completed, plese don't use!).");
src = pdf_lookup_dict(attr, "src");
if (!src) {
spc_warn(spe, "\"src\" attribute not found for \"img\" tag!");
return -1;
}
transform_info_clear(&ti);
obj = pdf_lookup_dict(attr, "width");
if (obj) {
ti.width = atopt(pdf_string_value(obj));
ti.flags |= INFO_HAS_WIDTH;
}
obj = pdf_lookup_dict(attr, "height");
if (obj) {
ti.height = atopt(pdf_string_value(obj));
ti.flags |= INFO_HAS_HEIGHT;
}
#ifdef ENABLE_HTML_SVG_OPACITY
obj = pdf_lookup_dict(attr, "svg:opacity");
if (obj) {
alpha = atof(pdf_string_value(obj));
if (alpha < 0.0 || alpha > 1.0) {
spc_warn(spe, "Invalid opacity value: %s", pdf_string_value(obj));
alpha = 1.0;
}
}
#endif /* ENABLE_HTML_SVG_OPCAITY */
#ifdef ENABLE_HTML_SVG_TRANSFORM
obj = pdf_lookup_dict(attr, "svg:transform");
if (obj) {
char *p = (char *) pdf_string_value(obj);
pdf_tmatrix N;
for ( ; *p && isspace(*p); p++);
while (*p && !error) {
pdf_setmatrix(&N, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
error = cvt_a_to_tmatrix(&N, p, &p);
if (!error) {
N.f = -N.f;
pdf_concatmatrix(&M, &N);
for ( ; *p && isspace(*p); p++);
if (*p == ',')
for (++p; *p && isspace(*p); p++);
}
}
}
#endif /* ENABLE_HTML_SVG_TRANSFORM */
if (error) {
spc_warn(spe, "Error in html \"img\" tag attribute.");
return error;
}
id = pdf_ximage_findresource(pdf_string_value(src), 0/*, 0*/);
if (id < 0) {
spc_warn(spe, "Could not find/load image: %s", pdf_string_value(src));
error = -1;
} else {
#if defined(ENABLE_HTML_SVG_TRANSFORM) || defined(ENABLE_HTML_SVG_OPACITY)
{
char *res_name;
pdf_rect r;
graphics_mode();
pdf_dev_gsave();
#ifdef ENABLE_HTML_SVG_OPACITY
{
pdf_obj *dict;
int a = round(100.0 * alpha);
if (a != 0) {
res_name = NEW(strlen("_Tps_a100_") + 1, char);
sprintf(res_name, "_Tps_a%03d_", a); /* Not Tps prefix but... */
if (!check_resourcestatus("ExtGState", res_name)) {
dict = create_xgstate(round_at(0.01 * a, 0.01), 0);
pdf_doc_add_page_resource("ExtGState",
res_name, pdf_ref_obj(dict));
pdf_release_obj(dict);
}
pdf_doc_add_page_content(" /", 2);
pdf_doc_add_page_content(res_name, strlen(res_name));
pdf_doc_add_page_content(" gs", 3);
RELEASE(res_name);
}
}
#endif /* ENABLE_HTML_SVG_OPACITY */
pdf_dev_concat(&M);
pdf_ximage_scale_image(id, &M, &r, &ti);
pdf_dev_concat(&M);
pdf_dev_rectclip(r.llx, r.lly, r.urx - r.llx, r.ury - r.lly);
res_name = pdf_ximage_get_resname(id);
pdf_doc_add_page_content(" /", 2);
pdf_doc_add_page_content(res_name, strlen(res_name));
pdf_doc_add_page_content(" Do", 3);
pdf_dev_grestore();
pdf_doc_add_page_resource("XObject",
res_name,
pdf_ximage_get_reference(id));
}
#else
pdf_dev_put_image(id, &ti, spe->x_user, spe->y_user);
#endif /* ENABLE_HTML_SVG_XXX */
}
return error;
}
#else
static int
spc_html__img_empty (struct spc_env *spe, pdf_obj *attr, struct spc_html_ *sd)
{
spc_warn(spe, "IMG tag not yet supported yet...");
return -1;
}
#endif /* ENABLE_HTML_IMG_SUPPORT */
static int
spc_handler_html_default (struct spc_env *spe, struct spc_arg *ap)
{
struct spc_html_ *sd = &_html_state;
char name[HTML_TAG_NAME_MAX + 1];
pdf_obj *attr;
int error = 0, type = HTML_TAG_TYPE_OPEN;
if (ap->curptr >= ap->endptr)
return 0;
attr = pdf_new_dict();
error = read_html_tag(name, attr, &type, &ap->curptr, ap->endptr);
if (error) {
pdf_release_obj(attr);
return error;
}
if (!strcmp(name, "a")) {
switch (type) {
case HTML_TAG_TYPE_OPEN:
error = spc_html__anchor_open (spe, attr, sd);
break;
case HTML_TAG_TYPE_CLOSE:
error = spc_html__anchor_close(spe, attr, sd);
break;
default:
spc_warn(spe, "Empty html anchor tag???");
error = -1;
break;
}
} else if (!strcmp(name, "base")) {
if (type == HTML_TAG_TYPE_CLOSE) {
spc_warn(spe, "Close tag for \"base\"???");
error = -1;
} else { /* treat "open" same as "empty" */
error = spc_html__base_empty(spe, attr, sd);
}
} else if (!strcmp(name, "img")) {
if (type == HTML_TAG_TYPE_CLOSE) {
spc_warn(spe, "Close tag for \"img\"???");
error = -1;
} else { /* treat "open" same as "empty" */
error = spc_html__img_empty(spe, attr, sd);
}
}
pdf_release_obj(attr);
for ( ; ap->curptr < ap->endptr && isspace(ap->curptr[0]); ap->curptr++);
return error;
}
#ifdef ENABLE_HTML_SVG_TRANSFORM
/* translate wsp* '(' wsp* number (comma-wsp number)? wsp* ')' */
static int
cvt_a_to_tmatrix (pdf_tmatrix *M, const char *ptr, char **nextptr)
{
char *q, *p = (char *) ptr;
int n;
double v[6];
static const char *_tkeys[] = {
#define K_TRNS__MATRIX 0
"matrix", /* a b c d e f */
#define K_TRNS__TRANSLATE 1
"translate", /* tx [ty] : dflt. tf = 0 */
#define K_TRNS__SCALE 2
"scale", /* sx [sy] : dflt. sy = sx */
#define K_TRNS__ROTATE 3
"rotate", /* ang [cx cy] : dflt. cx, cy = 0 */
#define K_TRNS__SKEWX 4
#define K_TRNS__SKEWY 5
"skewX", /* ang */
"skewY", /* ang */
NULL
};
int k;
for ( ; *p && isspace(*p); p++);
q = parse_c_ident(&p, p + strlen(p));
if (!q)
return -1;
/* parsed transformation key */
for (k = 0; _tkeys[k] && strcmp(q, _tkeys[k]); k++);
RELEASE(q);
/* handle args */
for ( ; *p && isspace(*p); p++);
if (*p != '(' || *(p + 1) == 0)
return -1;
for (++p; *p && isspace(*p); p++);
for (n = 0; n < 6 && *p && *p != ')'; n++) {
q = parse_float_decimal(&p, p + strlen(p));
if (!q)
break;
else {
v[n] = atof(q);
if (*p == ',')
p++;
for ( ; *p && isspace(*p); p++);
if (*p == ',')
for (++p; *p && isspace(*p); p++);
RELEASE(q);
}
}
if (*p != ')')
return -1;
p++;
switch (k) {
case K_TRNS__MATRIX:
if (n != 6)
return -1;
M->a = v[0]; M->c = v[1];
M->b = v[2]; M->d = v[3];
M->e = v[4]; M->f = v[5];
break;
case K_TRNS__TRANSLATE:
if (n != 1 && n != 2)
return -1;
M->a = M->d = 1.0;
M->c = M->b = 0.0;
M->e = v[0]; M->f = (n == 2) ? v[1] : 0.0;
break;
case K_TRNS__SCALE:
if (n != 1 && n != 2)
return -1;
M->a = v[0]; M->d = (n == 2) ? v[1] : v[0];
M->c = M->b = 0.0;
M->e = M->f = 0.0;
break;
case K_TRNS__ROTATE:
if (n != 1 && n != 3)
return -1;
M->a = cos(v[0] * M_PI / 180.0);
M->c = sin(v[0] * M_PI / 180.0);
M->b = -M->c; M->d = M->a;
M->e = (n == 3) ? v[1] : 0.0;
M->f = (n == 3) ? v[2] : 0.0;
break;
case K_TRNS__SKEWX:
if (n != 1)
return -1;
M->a = M->d = 1.0;
M->c = 0.0;
M->b = tan(v[0] * M_PI / 180.0);
break;
case K_TRNS__SKEWY:
if (n != 1)
return -1;
M->a = M->d = 1.0;
M->c = tan(v[0] * M_PI / 180.0);
M->b = 0.0;
break;
}
if (nextptr)
*nextptr = p;
return 0;
}
#endif /* ENABLE_HTML_SVG_TRANSFORM */
int
spc_html_at_begin_document (void)
{
struct spc_html_ *sd = &_html_state;
return spc_handler_html__init(NULL, NULL, sd);
}
int
spc_html_at_begin_page (void)
{
struct spc_html_ *sd = &_html_state;
return spc_handler_html__bophook(NULL, NULL, sd);
}
int
spc_html_at_end_page (void)
{
struct spc_html_ *sd = &_html_state;
return spc_handler_html__eophook(NULL, NULL, sd);
}
int
spc_html_at_end_document (void)
{
struct spc_html_ *sd = &_html_state;
return spc_handler_html__clean(NULL, NULL, sd);
}
int
spc_html_check_special (const char *buffer, long size)
{
char *p, *endptr;
p = (char *) buffer;
endptr = p + size;
for ( ; p < endptr && isspace(*p); p++);
size = (long) (endptr - p);
if (size >= strlen("html:") &&
!memcmp(p, "html:", strlen("html:"))) {
return 1;
}
return 0;
}
int
spc_html_setup_handler (struct spc_handler *sph,
struct spc_env *spe, struct spc_arg *ap)
{
ASSERT(sph && spe && ap);
for ( ; ap->curptr < ap->endptr && isspace(ap->curptr[0]); ap->curptr++);
if (ap->curptr + strlen("html:") > ap->endptr ||
memcmp(ap->curptr, "html:", strlen("html:"))) {
return -1;
}
ap->command = (char *) "";
sph->key = (char *) "html:";
sph->exec = &spc_handler_html_default;
ap->curptr += strlen("html:");
for ( ; ap->curptr < ap->endptr && isspace(ap->curptr[0]); ap->curptr++);
return 0;
}