updf: remove xps support as non-used

git-svn-id: svn://kolibrios.org@7621 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
Kirill Lipatov (Leency) 2019-04-03 15:49:01 +00:00
parent 42c754192f
commit 12dca64514
25 changed files with 18 additions and 6923 deletions

View File

@ -7,7 +7,7 @@ GEN := generated
default: all default: all
LDFLAGS = -Tinclude/scripts/menuetos_app_v01.ld -nostdlib -L include/lib LDFLAGS = -Tinclude/scripts/menuetos_app_v01.ld -nostdlib -L include/lib
CFLAGS += -Ifitz -Ipdf -Ixps -Iscripts -fno-stack-protector -nostdinc -fno-builtin -m32 -I include -fno-pic -w CFLAGS += -Ifitz -Ipdf -Iscripts -fno-stack-protector -nostdinc -fno-builtin -m32 -I include -fno-pic -w
LIBS += -lfreetype2 -lpng -ljbig2dec -ljpeg -lopenjpeg -lz -lm -lc LIBS += -lfreetype2 -lpng -ljbig2dec -ljpeg -lopenjpeg -lz -lm -lc
#include Makerules #include Makerules
@ -49,31 +49,26 @@ $(OUT)/%.o : draw/%.c fitz/fitz.h | $(OUT)
$(CC_CMD) $(CC_CMD)
$(OUT)/%.o : pdf/%.c fitz/fitz.h pdf/mupdf.h | $(OUT) $(OUT)/%.o : pdf/%.c fitz/fitz.h pdf/mupdf.h | $(OUT)
$(CC_CMD) $(CC_CMD)
$(OUT)/%.o : xps/%.c fitz/fitz.h xps/muxps.h | $(OUT) $(OUT)/%.o : apps/%.c fitz/fitz.h pdf/mupdf.h | $(OUT)
$(CC_CMD)
$(OUT)/%.o : apps/%.c fitz/fitz.h pdf/mupdf.h xps/muxps.h | $(OUT)
$(CC_CMD) $(CC_CMD)
$(OUT)/%.o : scripts/%.c | $(OUT) $(OUT)/%.o : scripts/%.c | $(OUT)
$(CC_CMD) $(CC_CMD)
.PRECIOUS : $(OUT)/%.o # Keep intermediates from chained rules .PRECIOUS : $(OUT)/%.o # Keep intermediates from chained rules
# --- Fitz, MuPDF and MuXPS libraries --- # --- Fitz and MuPDF libraries ---
FITZ_LIB := $(OUT)/libfitz.a FITZ_LIB := $(OUT)/libfitz.a
MUPDF_LIB := $(OUT)/libmupdf.a MUPDF_LIB := $(OUT)/libmupdf.a
MUXPS_LIB := $(OUT)/libmuxps.a
FITZ_SRC := $(notdir $(wildcard fitz/*.c draw/*.c)) FITZ_SRC := $(notdir $(wildcard fitz/*.c draw/*.c))
MUPDF_SRC := $(notdir $(wildcard pdf/*.c)) MUPDF_SRC := $(notdir $(wildcard pdf/*.c))
MUXPS_SRC := $(notdir $(wildcard xps/*.c))
$(FITZ_LIB) : $(addprefix $(OUT)/, $(FITZ_SRC:%.c=%.o)) $(FITZ_LIB) : $(addprefix $(OUT)/, $(FITZ_SRC:%.c=%.o))
$(MUPDF_LIB) : $(addprefix $(OUT)/, $(MUPDF_SRC:%.c=%.o)) $(MUPDF_LIB) : $(addprefix $(OUT)/, $(MUPDF_SRC:%.c=%.o))
$(MUXPS_LIB) : $(addprefix $(OUT)/, $(MUXPS_SRC:%.c=%.o))
libs: $(MUXPS_LIB) $(MUPDF_LIB) $(FITZ_LIB) $(THIRD_LIBS) libs: $(MUPDF_LIB) $(FITZ_LIB) $(THIRD_LIBS)
@ echo MuPDF/XPS and underlying libraries built @ echo MuPDF and underlying libraries built
# --- Generated CMAP and FONT files --- # --- Generated CMAP and FONT files ---
@ -121,40 +116,14 @@ $(OUT)/cmapdump.o : pdf/pdf_cmap.c pdf/pdf_cmap_parse.c
# --- Tools and Apps --- # --- Tools and Apps ---
PDF_APPS := $(addprefix $(OUT)/, pdfdraw pdfclean pdfextract pdfinfo pdfshow) PDF_APPS := $(addprefix $(OUT)/, pdfdraw pdfclean pdfextract pdfinfo pdfshow)
XPS_APPS := $(addprefix $(OUT)/, xpsdraw)
$(PDF_APPS) : $(MUPDF_LIB) $(FITZ_LIB) $(THIRD_LIBS) $(PDF_APPS) : $(MUPDF_LIB) $(FITZ_LIB) $(THIRD_LIBS)
$(XPS_APPS) : $(MUXPS_LIB) $(FITZ_LIB) $(THIRD_LIBS)
MUPDF := $(OUT)/mupdf MUPDF := $(OUT)/mupdf
$(MUPDF) : $(MUXPS_LIB) $(MUPDF_LIB) $(FITZ_LIB) $(THIRD_LIBS) $(MUPDF) : $(MUPDF_LIB) $(FITZ_LIB) $(THIRD_LIBS)
ifeq "$(NOX11)" "" ifeq "$(NOX11)" ""
$(MUPDF) : $(addprefix $(OUT)/, kos_main.o pdfapp.o) $(MUPDF) : $(addprefix $(OUT)/, kos_main.o pdfapp.o)
$(LINK_CMD) $(LINK_CMD)
endif endif
# --- Install --- all: $(THIRD_LIBS) $(FITZ_LIB) $(PDF_APPS) $(MUPDF)
prefix ?= /usr/local
bindir ?= $(prefix)/bin
libdir ?= $(prefix)/lib
incdir ?= $(prefix)/include
mandir ?= $(prefix)/share/man
install: $(MUXPS_LIB) $(MUPDF_LIB) $(FITZ_LIB) $(PDF_APPS) $(XPS_APPS) $(MUPDF)
install -d $(bindir) $(libdir) $(incdir) $(mandir)/man1
install $(MUXPS_LIB) $(MUPDF_LIB) $(FITZ_LIB) $(libdir)
install fitz/fitz.h pdf/mupdf.h xps/muxps.h $(incdir)
install $(PDF_APPS) $(XPS_APPS) $(MUPDF) $(bindir)
install $(wildcard apps/man/*.1) $(mandir)/man1
# --- Clean and Default ---
all: $(THIRD_LIBS) $(FITZ_LIB) $(PDF_APPS) $(XPS_APPS) $(MUPDF)
clean:
rm -rf $(OUT)
nuke:
rm -rf build/* $(GEN)
.PHONY: all clean nuke install

View File

@ -1,84 +0,0 @@
# Configuration for the Makefile
OS ?= $(shell uname)
OS := $(OS:MINGW%=MINGW)
CFLAGS += -Wall
ifeq "$(build)" "debug"
CFLAGS += -pipe -g
else ifeq "$(build)" "profile"
CFLAGS += -pipe -O2 -DNDEBUG -pg
LDFLAGS += -pg
else ifeq "$(build)" "release"
CFLAGS += -pipe -O2 -DNDEBUG -fomit-frame-pointer
else ifeq "$(build)" "native"
CFLAGS += -pipe -O2 -DNDEBUG -fomit-frame-pointer -march=native -mfpmath=sse
else
$(error unknown build setting: '$(build)')
endif
ifeq "$(OS)" "Linux"
SYS_FREETYPE_INC := `pkg-config --cflags freetype2`
X11_LIBS := -lX11 -lXext
endif
ifeq "$(OS)" "FreeBSD"
SYS_FREETYPE_INC := `pkg-config --cflags freetype2`
LDFLAGS += -L/usr/local/lib
X11_LIBS := -lX11 -lXext
endif
# Mac OS X build depends on some thirdparty libs
ifeq "$(OS)" "Darwin"
SYS_FREETYPE_INC := -I/usr/X11R6/include/freetype2
CFLAGS += -I/usr/X11R6/include
LDFLAGS += -L/usr/X11R6/lib
X11_LIBS := -lX11 -lXext
ifeq "$(arch)" "amd64"
CFLAGS += -m64
LDFLAGS += -m64
else
CFLAGS += -m32
LDFLAGS += -m32
endif
endif
# The following section is an example of how to simply do cross-compilation
# using these Makefiles. It builds for a beagleboard running ARM linux,
# compiling on windows with the CodeSourcery G++ compilers.
# Invoke this as:
# make OS=beagle-cross build=release
# This does rely on the generated directory being populated with the cmap
# files etc first. Either:
# 1) do 'make generate' first (this relies on you having an appropriate host
# base C compiler set up - such as you would have on unix or in windows
# cygwin)
# 2) do a non cross compile build (e.g. windows in MSVC) first.
# 3) download the generated files from mupdf.com.
ifeq "$(OS)" "beagle-cross"
CC = arm-none-linux-gnueabi-gcc
LD = arm-none-linux-gnueabi-gcc
AR = arm-none-linux-gnueabi-ar
CFLAGS += -O3 -mfpu=neon -mcpu=cortex-a8 -mfloat-abi=softfp -ftree-vectorize -ffast-math -fsingle-precision-constant
CROSSCOMPILE=yes
NOX11=yes
endif
ifeq "$(OS)" "webos-pre-cross"
CC = arm-none-linux-gnueabi-gcc
LD = arm-none-linux-gnueabi-gcc
AR = arm-none-linux-gnueabi-ar
CFLAGS += -O3 -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp -ftree-vectorize -ffast-math -fsingle-precision-constant
CROSSCOMPILE=yes
NOX11=yes
endif
ifeq "$(OS)" "webos-pixi-cross"
CC = arm-none-linux-gnueabi-gcc
LD = arm-none-linux-gnueabi-gcc
AR = arm-none-linux-gnueabi-ar
CFLAGS += -O3 -mcpu=arm1136jf-s -mfpu=vfp -mfloat-abi=softfp -ffast-math -fsingle-precision-constant
CROSSCOMPILE=yes
NOX11=yes
endif

View File

@ -1,203 +0,0 @@
# GNU Makefile for third party libraries used by MuPDF
#
# If thirdparty libraries are supplied, they will be built as
# static libraries. Download and unzip the the mupdf-thirdparty.zip
# archive in the source directory.
FREETYPE_DIR := $(wildcard thirdparty/freetype*)
JBIG2DEC_DIR := $(wildcard thirdparty/jbig2dec*)
JPEG_DIR := $(wildcard thirdparty/jpeg*)
OPENJPEG_DIR := $(wildcard thirdparty/openjpeg*/libopenjpeg)
ZLIB_DIR := $(wildcard thirdparty/zlib*)
# --- FreeType 2 ---
ifneq "$(FREETYPE_DIR)" ""
CFLAGS += -I$(FREETYPE_DIR)/include
LIBS := $(filter-out -lfreetype, $(LIBS))
FREETYPE_LIB := $(OUT)/libfreetype.a
FREETYPE_SRC := \
ftbase.c \
ftbbox.c \
ftbitmap.c \
ftgasp.c \
ftglyph.c \
ftinit.c \
ftstroke.c \
ftsynth.c \
ftsystem.c \
fttype1.c \
ftxf86.c \
cff.c \
psaux.c \
pshinter.c \
psnames.c \
raster.c \
sfnt.c \
smooth.c \
truetype.c \
type1.c \
type1cid.c \
$(FREETYPE_LIB): $(addprefix $(OUT)/ft_, $(FREETYPE_SRC:%.c=%.o))
FT_CFLAGS := -DFT2_BUILD_LIBRARY -DDARWIN_NO_CARBON \
'-DFT_CONFIG_MODULES_H="slimftmodules.h"' \
'-DFT_CONFIG_OPTIONS_H="slimftoptions.h"'
$(OUT)/ft_%.o: $(FREETYPE_DIR)/src/base/%.c | $(OUT)
$(CC_CMD) $(FT_CFLAGS)
$(OUT)/ft_%.o: $(FREETYPE_DIR)/src/cff/%.c | $(OUT)
$(CC_CMD) $(FT_CFLAGS)
$(OUT)/ft_%.o: $(FREETYPE_DIR)/src/cid/%.c | $(OUT)
$(CC_CMD) $(FT_CFLAGS)
$(OUT)/ft_%.o: $(FREETYPE_DIR)/src/psaux/%.c | $(OUT)
$(CC_CMD) $(FT_CFLAGS)
$(OUT)/ft_%.o: $(FREETYPE_DIR)/src/pshinter/%.c | $(OUT)
$(CC_CMD) $(FT_CFLAGS)
$(OUT)/ft_%.o: $(FREETYPE_DIR)/src/psnames/%.c | $(OUT)
$(CC_CMD) $(FT_CFLAGS)
$(OUT)/ft_%.o: $(FREETYPE_DIR)/src/raster/%.c | $(OUT)
$(CC_CMD) $(FT_CFLAGS)
$(OUT)/ft_%.o: $(FREETYPE_DIR)/src/smooth/%.c | $(OUT)
$(CC_CMD) $(FT_CFLAGS)
$(OUT)/ft_%.o: $(FREETYPE_DIR)/src/sfnt/%.c | $(OUT)
$(CC_CMD) $(FT_CFLAGS)
$(OUT)/ft_%.o: $(FREETYPE_DIR)/src/truetype/%.c | $(OUT)
$(CC_CMD) $(FT_CFLAGS)
$(OUT)/ft_%.o: $(FREETYPE_DIR)/src/type1/%.c | $(OUT)
$(CC_CMD) $(FT_CFLAGS)
else
CFLAGS += $(SYS_FREETYPE_INC)
endif
# --- JBIG2DEC ---
ifneq "$(JBIG2DEC_DIR)" ""
CFLAGS += -I$(JBIG2DEC_DIR)
LIBS := $(filter-out -ljbig2dec, $(LIBS))
JBIG2DEC_LIB := $(OUT)/libjbig2dec.a
JBIG2DEC_SRC := \
jbig2.c \
jbig2_arith.c \
jbig2_arith_iaid.c \
jbig2_arith_int.c \
jbig2_generic.c \
jbig2_huffman.c \
jbig2_image.c \
jbig2_metadata.c \
jbig2_mmr.c \
jbig2_page.c \
jbig2_refinement.c \
jbig2_segment.c \
jbig2_symbol_dict.c \
jbig2_text.c \
$(JBIG2DEC_LIB): $(addprefix $(OUT)/, $(JBIG2DEC_SRC:%.c=%.o))
$(OUT)/%.o: $(JBIG2DEC_DIR)/%.c | $(OUT)
$(CC_CMD) -DHAVE_STDINT_H
endif
# --- JPEG library from IJG ---
ifneq "$(JPEG_DIR)" ""
CFLAGS += -I$(JPEG_DIR)
LIBS := $(filter-out -ljpeg, $(LIBS))
JPEG_LIB := $(OUT)/libjpeg.a
JPEG_SRC := \
jaricom.c \
jcomapi.c \
jdapimin.c \
jdapistd.c \
jdarith.c \
jdatadst.c \
jdatasrc.c \
jdcoefct.c \
jdcolor.c \
jddctmgr.c \
jdhuff.c \
jdinput.c \
jdmainct.c \
jdmarker.c \
jdmaster.c \
jdmerge.c \
jdpostct.c \
jdsample.c \
jdtrans.c \
jerror.c \
jfdctflt.c \
jfdctfst.c \
jfdctint.c \
jidctflt.c \
jidctfst.c \
jidctint.c \
jmemansi.c \
jmemmgr.c \
jquant1.c \
jquant2.c \
jutils.c \
$(JPEG_LIB): $(addprefix $(OUT)/jpeg_, $(JPEG_SRC:%.c=%.o))
$(OUT)/jpeg_%.o: $(JPEG_DIR)/%.c | $(OUT)
$(CC_CMD) -Dmain=xxxmain
endif
# --- OpenJPEG ---
ifneq "$(OPENJPEG_DIR)" ""
CFLAGS += -I$(OPENJPEG_DIR)
LIBS := $(filter-out -lopenjpeg, $(LIBS))
OPENJPEG_LIB := $(OUT)/libopenjpeg.a
OPENJPEG_SRC := \
bio.c \
cio.c \
dwt.c \
event.c \
image.c \
j2k.c \
j2k_lib.c \
jp2.c \
jpt.c \
mct.c \
mqc.c \
openjpeg.c \
pi.c \
raw.c \
t1.c \
t2.c \
tcd.c \
tgt.c \
$(OPENJPEG_LIB): $(addprefix $(OUT)/opj_, $(OPENJPEG_SRC:%.c=%.o))
$(OUT)/opj_%.o: $(OPENJPEG_DIR)/%.c | $(OUT)
$(CC_CMD) -DOPJ_STATIC
endif
# --- ZLIB ---
ifneq "$(ZLIB_DIR)" ""
CFLAGS += -I$(ZLIB_DIR)
LIBS := $(filter-out -lz, $(LIBS))
ZLIB_LIB := $(OUT)/libz.a
ZLIB_SRC := \
adler32.c \
compress.c \
crc32.c \
deflate.c \
inffast.c \
inflate.c \
inftrees.c \
trees.c \
uncompr.c \
zutil.c \
$(ZLIB_LIB): $(addprefix $(OUT)/zlib_, $(ZLIB_SRC:%.c=%.o))
$(OUT)/zlib_%.o: $(ZLIB_DIR)/%.c | $(OUT)
$(CC_CMD)
endif

View File

@ -2,7 +2,6 @@
#define _WIN32 #define _WIN32
#include "fitz.h" #include "fitz.h"
#include "mupdf.h" #include "mupdf.h"
#include "muxps.h"
#include "pdfapp.h" #include "pdfapp.h"
#include "icons/allbtns.h" #include "icons/allbtns.h"
#include "kolibri.c" #include "kolibri.c"

View File

@ -1,6 +1,5 @@
#include "fitz.h" #include "fitz.h"
#include "mupdf.h" #include "mupdf.h"
#include "muxps.h"
#include "pdfapp.h" #include "pdfapp.h"
#include <ctype.h> /* for tolower() */ #include <ctype.h> /* for tolower() */
@ -66,7 +65,8 @@ char *pdfapp_usage(pdfapp_t *app)
"n\t\t-- find next search result\n" "n\t\t-- find next search result\n"
"N\t\t-- find previous search result\n" "N\t\t-- find previous search result\n"
"c\t\t-- toggle between color and grayscale\n" "c\t\t-- toggle between color and grayscale\n"
; */ ;
*/
} }
void pdfapp_init(pdfapp_t *app) void pdfapp_init(pdfapp_t *app)
@ -172,28 +172,9 @@ __menuet__debug_out("Page counter\n");
__menuet__debug_out("All is set!\n"); __menuet__debug_out("All is set!\n");
} }
static void pdfapp_open_xps(pdfapp_t *app, char *filename, int fd)
{
fz_error error;
fz_stream *file;
file = fz_open_fd(fd);
error = xps_open_stream(&app->xps, file);
if (error)
pdfapp_error(app, fz_rethrow(error, "cannot open document '%s'", filename));
fz_close(file);
app->doctitle = filename;
app->pagecount = xps_count_pages(app->xps);
}
void pdfapp_open(pdfapp_t *app, char *filename, int fd, int reload) void pdfapp_open(pdfapp_t *app, char *filename, int fd, int reload)
{ {
if (strstr(filename, ".xps") || strstr(filename, ".XPS") || strstr(filename, ".rels")) pdfapp_open_pdf(app, filename, fd);
pdfapp_open_xps(app, filename, fd);
else
pdfapp_open_pdf(app, filename, fd);
app->cache = fz_new_glyph_cache(); app->cache = fz_new_glyph_cache();
@ -241,12 +222,6 @@ void pdfapp_close(pdfapp_t *app)
app->xref = NULL; app->xref = NULL;
} }
if (app->xps)
{
xps_free_context(app->xps);
app->xps = NULL;
}
fz_flush_warnings(); fz_flush_warnings();
} }
@ -333,34 +308,6 @@ static void pdfapp_loadpage_pdf(pdfapp_t *app)
pdf_age_store(app->xref->store, 3); pdf_age_store(app->xref->store, 3);
} }
static void pdfapp_loadpage_xps(pdfapp_t *app)
{
xps_page *page;
fz_device *mdev;
fz_error error;
error = xps_load_page(&page, app->xps, app->pageno - 1);
if (error)
pdfapp_error(app, fz_rethrow(error, "cannot load page %d in file '%s'", app->pageno, app->doctitle));
app->page_bbox.x0 = 0;
app->page_bbox.y0 = 0;
app->page_bbox.x1 = page->width;
app->page_bbox.y1 = page->height;
app->page_rotate = 0;
app->page_links = NULL;
/* Create display list */
app->page_list = fz_new_display_list();
mdev = fz_new_list_device(app->page_list);
app->xps->dev = mdev;
xps_parse_fixed_page(app->xps, fz_identity, page);
app->xps->dev = NULL;
fz_free_device(mdev);
xps_free_page(app->xps, page);
}
static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repaint) static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repaint)
{ {
char buf[256]; char buf[256];
@ -383,8 +330,6 @@ static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repai
if (app->xref) if (app->xref)
pdfapp_loadpage_pdf(app); pdfapp_loadpage_pdf(app);
if (app->xps)
pdfapp_loadpage_xps(app);
/* Zero search hit position */ /* Zero search hit position */
app->hit = -1; app->hit = -1;
@ -399,9 +344,9 @@ static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repai
if (drawpage) if (drawpage)
{ {
// sprintf(buf, "%s - %d/%d (%d dpi)", app->doctitle, sprintf(buf, "%s - %d/%d (%d dpi)", app->doctitle,
// app->pageno, app->pagecount, app->resolution); app->pageno, app->pagecount, app->resolution);
// wintitle(app, buf); wintitle(app, buf);
ctm = pdfapp_viewctm(app); ctm = pdfapp_viewctm(app);
bbox = fz_round_rect(fz_transform_rect(ctm, app->page_bbox)); bbox = fz_round_rect(fz_transform_rect(ctm, app->page_bbox));
@ -430,7 +375,7 @@ static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repai
if (app->shrinkwrap) if (app->shrinkwrap)
{ {
//__menuet__debug_out ("SHRINK\n"); __menuet__debug_out ("SHRINK\n");
int w = app->image->w; int w = app->image->w;
int h = app->image->h; int h = app->image->h;
if (app->winw == w) if (app->winw == w)

View File

@ -32,7 +32,6 @@ struct pdfapp_s
char *doctitle; char *doctitle;
pdf_xref *xref; pdf_xref *xref;
pdf_outline *outline; pdf_outline *outline;
xps_context *xps;
int pagecount; int pagecount;
fz_glyph_cache *cache; fz_glyph_cache *cache;

View File

@ -1,366 +0,0 @@
#include "fitz.h"
#include "muxps.h"
#ifdef _MSC_VER
#include <winsock2.h>
#else
#include <sys/time.h>
#endif
char *output = NULL;
float resolution = 72;
int showxml = 0;
int showtext = 0;
int showtime = 0;
int showmd5 = 0;
int savealpha = 0;
int uselist = 1;
fz_colorspace *colorspace;
fz_glyph_cache *glyphcache;
char *filename;
struct {
int count, total;
int min, max;
int minpage, maxpage;
} timing;
static void die(fz_error error)
{
fz_catch(error, "aborting");
exit(1);
}
static void usage(void)
{
fprintf(stderr,
"usage: xpsdraw [options] input.xps [pages]\n"
"\t-o -\toutput filename (%%d for page number)\n"
"\t\tsupported formats: pgm, ppm, pam, png\n"
"\t-r -\tresolution in dpi (default: 72)\n"
"\t-a\tsave alpha channel (only pam and png)\n"
"\t-g\trender in grayscale\n"
"\t-m\tshow timing information\n"
"\t-t\tshow text (-tt for xml)\n"
"\t-x\tshow display list\n"
"\t-d\tdisable use of display list\n"
"\t-5\tshow md5 checksums\n"
"\tpages\tcomma separated list of ranges\n");
exit(1);
}
static int gettime(void)
{
static struct timeval first;
static int once = 1;
struct timeval now;
if (once)
{
gettimeofday(&first, NULL);
once = 0;
}
gettimeofday(&now, NULL);
return (now.tv_sec - first.tv_sec) * 1000 + (now.tv_usec - first.tv_usec) / 1000;
}
static int isrange(char *s)
{
while (*s)
{
if ((*s < '0' || *s > '9') && *s != '-' && *s != ',')
return 0;
s++;
}
return 1;
}
static void
xps_run_page(xps_context *ctx, xps_page *page, fz_device *dev, fz_matrix ctm)
{
ctx->dev = dev;
xps_parse_fixed_page(ctx, ctm, page);
ctx->dev = NULL;
}
static void drawpage(xps_context *ctx, int pagenum)
{
xps_page *page;
fz_display_list *list;
fz_device *dev;
int start;
int code;
if (showtime)
{
start = gettime();
}
code = xps_load_page(&page, ctx, pagenum - 1);
if (code)
die(fz_rethrow(code, "cannot load page %d in file '%s'", pagenum, filename));
list = NULL;
if (uselist)
{
list = fz_new_display_list();
dev = fz_new_list_device(list);
xps_run_page(ctx, page, dev, fz_identity);
fz_free_device(dev);
}
if (showxml)
{
dev = fz_new_trace_device();
printf("<page number=\"%d\">\n", pagenum);
if (list)
fz_execute_display_list(list, dev, fz_identity, fz_infinite_bbox);
else
xps_run_page(ctx, page, dev, fz_identity);
printf("</page>\n");
fz_free_device(dev);
}
if (showtext)
{
fz_text_span *text = fz_new_text_span();
dev = fz_new_text_device(text);
if (list)
fz_execute_display_list(list, dev, fz_identity, fz_infinite_bbox);
else
xps_run_page(ctx, page, dev, fz_identity);
fz_free_device(dev);
printf("[Page %d]\n", pagenum);
if (showtext > 1)
fz_debug_text_span_xml(text);
else
fz_debug_text_span(text);
printf("\n");
fz_free_text_span(text);
}
if (showmd5 || showtime)
printf("page %s %d", filename, pagenum);
if (output || showmd5 || showtime)
{
float zoom;
fz_matrix ctm;
fz_rect rect;
fz_bbox bbox;
fz_pixmap *pix;
rect.x0 = rect.y0 = 0;
rect.x1 = page->width;
rect.y1 = page->height;
zoom = resolution / 96;
ctm = fz_translate(0, -page->height);
ctm = fz_concat(ctm, fz_scale(zoom, zoom));
bbox = fz_round_rect(fz_transform_rect(ctm, rect));
/* TODO: banded rendering and multi-page ppm */
pix = fz_new_pixmap_with_rect(colorspace, bbox);
if (savealpha)
fz_clear_pixmap(pix);
else
fz_clear_pixmap_with_color(pix, 255);
dev = fz_new_draw_device(glyphcache, pix);
if (list)
fz_execute_display_list(list, dev, ctm, bbox);
else
xps_run_page(ctx, page, dev, ctm);
fz_free_device(dev);
if (output)
{
char buf[512];
sprintf(buf, output, pagenum);
if (strstr(output, ".pgm") || strstr(output, ".ppm") || strstr(output, ".pnm"))
fz_write_pnm(pix, buf);
else if (strstr(output, ".pam"))
fz_write_pam(pix, buf, savealpha);
else if (strstr(output, ".png"))
fz_write_png(pix, buf, savealpha);
}
if (showmd5)
{
fz_md5 md5;
unsigned char digest[16];
int i;
fz_md5_init(&md5);
fz_md5_update(&md5, pix->samples, pix->w * pix->h * pix->n);
fz_md5_final(&md5, digest);
printf(" ");
for (i = 0; i < 16; i++)
printf("%02x", digest[i]);
}
fz_drop_pixmap(pix);
}
if (list)
fz_free_display_list(list);
if (showtime)
{
int end = gettime();
int diff = end - start;
if (diff < timing.min)
{
timing.min = diff;
timing.minpage = pagenum;
}
if (diff > timing.max)
{
timing.max = diff;
timing.maxpage = pagenum;
}
timing.total += diff;
timing.count ++;
printf(" %dms", diff);
}
if (showmd5 || showtime)
printf("\n");
}
static void drawrange(xps_context *ctx, char *range)
{
int page, spage, epage;
char *spec, *dash;
spec = fz_strsep(&range, ",");
while (spec)
{
dash = strchr(spec, '-');
if (dash == spec)
spage = epage = xps_count_pages(ctx);
else
spage = epage = atoi(spec);
if (dash)
{
if (strlen(dash) > 1)
epage = atoi(dash + 1);
else
epage = xps_count_pages(ctx);
}
spage = CLAMP(spage, 1, xps_count_pages(ctx));
epage = CLAMP(epage, 1, xps_count_pages(ctx));
if (spage < epage)
for (page = spage; page <= epage; page++)
drawpage(ctx, page);
else
for (page = spage; page >= epage; page--)
drawpage(ctx, page);
spec = fz_strsep(&range, ",");
}
}
int main(int argc, char **argv)
{
int grayscale = 0;
int accelerate = 1;
xps_context *ctx;
int code;
int c;
while ((c = fz_getopt(argc, argv, "o:p:r:Aadgmtx5")) != -1)
{
switch (c)
{
case 'o': output = fz_optarg; break;
case 'r': resolution = atof(fz_optarg); break;
case 'A': accelerate = 0; break;
case 'a': savealpha = 1; break;
case 'm': showtime++; break;
case 't': showtext++; break;
case 'x': showxml++; break;
case '5': showmd5++; break;
case 'g': grayscale++; break;
case 'd': uselist = 0; break;
default: usage(); break;
}
}
if (fz_optind == argc)
usage();
if (!showtext && !showxml && !showtime && !showmd5 && !output)
{
printf("nothing to do\n");
exit(0);
}
if (accelerate)
fz_accelerate();
glyphcache = fz_new_glyph_cache();
colorspace = fz_device_rgb;
if (grayscale)
colorspace = fz_device_gray;
if (output && strstr(output, ".pgm"))
colorspace = fz_device_gray;
if (output && strstr(output, ".ppm"))
colorspace = fz_device_rgb;
timing.count = 0;
timing.total = 0;
timing.min = 1 << 30;
timing.max = 0;
timing.minpage = 0;
timing.maxpage = 0;
if (showxml)
printf("<?xml version=\"1.0\"?>\n");
while (fz_optind < argc)
{
filename = argv[fz_optind++];
code = xps_open_file(&ctx, filename);
if (code)
die(fz_rethrow(code, "cannot open document: %s", filename));
if (showxml)
printf("<document name=\"%s\">\n", filename);
if (fz_optind == argc || !isrange(argv[fz_optind]))
drawrange(ctx, "1-");
if (fz_optind < argc && isrange(argv[fz_optind]))
drawrange(ctx, argv[fz_optind++]);
if (showxml)
printf("</document>\n");
xps_free_context(ctx);
}
if (showtime)
{
printf("total %dms / %d pages for an average of %dms\n",
timing.total, timing.count, timing.total / timing.count);
printf("fastest page %d: %dms\n", timing.minpage, timing.min);
printf("slowest page %d: %dms\n", timing.maxpage, timing.max);
}
fz_free_glyph_cache(glyphcache);
return 0;
}

View File

@ -619,7 +619,7 @@ FT_BEGIN_HEADER
#else /* current sources say */ #else /* current sources say */
#define TT_MS_LANGID_CROATIAN_BOSNIA_HERZEGOVINA 0x101a #define TT_MS_LANGID_CROATIAN_BOSNIA_HERZEGOVINA 0x101a
#define TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA 0x141a #define TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA 0x141a
/* and XPsp2 Platform SDK added (2004-07-26) */ /* and XP_sp2 Platform SDK added (2004-07-26) */
/* Names are shortened to be significant within 40 chars. */ /* Names are shortened to be significant within 40 chars. */
#define TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_LATIN 0x181a #define TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_LATIN 0x181a
#define TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_CYRILLIC 0x181a #define TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_CYRILLIC 0x181a
@ -661,7 +661,7 @@ FT_BEGIN_HEADER
#define TT_MS_LANGID_FAEROESE_FAEROE_ISLANDS 0x0438 #define TT_MS_LANGID_FAEROESE_FAEROE_ISLANDS 0x0438
#define TT_MS_LANGID_HINDI_INDIA 0x0439 #define TT_MS_LANGID_HINDI_INDIA 0x0439
#define TT_MS_LANGID_MALTESE_MALTA 0x043a #define TT_MS_LANGID_MALTESE_MALTA 0x043a
/* Added by XPsp2 Platform SDK (2004-07-26) */ /* Added by XP_sp2 Platform SDK (2004-07-26) */
#define TT_MS_LANGID_SAMI_NORTHERN_NORWAY 0x043b #define TT_MS_LANGID_SAMI_NORTHERN_NORWAY 0x043b
#define TT_MS_LANGID_SAMI_NORTHERN_SWEDEN 0x083b #define TT_MS_LANGID_SAMI_NORTHERN_SWEDEN 0x083b
#define TT_MS_LANGID_SAMI_NORTHERN_FINLAND 0x0C3b #define TT_MS_LANGID_SAMI_NORTHERN_FINLAND 0x0C3b
@ -751,7 +751,7 @@ FT_BEGIN_HEADER
#define TT_MS_LANGID_TAMAZIGHT_MOROCCO_LATIN 0x085f #define TT_MS_LANGID_TAMAZIGHT_MOROCCO_LATIN 0x085f
/* Missing a LCID for Tifinagh script */ /* Missing a LCID for Tifinagh script */
#define TT_MS_LANGID_KASHMIRI_PAKISTAN /* Arabic */ 0x0460 #define TT_MS_LANGID_KASHMIRI_PAKISTAN /* Arabic */ 0x0460
/* Spelled this way by XPsp2 Platform SDK (2004-07-26) */ /* Spelled this way by XP_sp2 Platform SDK (2004-07-26) */
/* script is yet unclear... might be Arabic, Nagari or Sharada */ /* script is yet unclear... might be Arabic, Nagari or Sharada */
#define TT_MS_LANGID_KASHMIRI_SASIA 0x0860 #define TT_MS_LANGID_KASHMIRI_SASIA 0x0860
/* ... and aliased (by MS) for compatibility reasons. */ /* ... and aliased (by MS) for compatibility reasons. */
@ -773,7 +773,7 @@ FT_BEGIN_HEADER
#define TT_MS_LANGID_QUECHUA_ECUADOR 0x086b #define TT_MS_LANGID_QUECHUA_ECUADOR 0x086b
#define TT_MS_LANGID_QUECHUA_PERU 0x0c6b #define TT_MS_LANGID_QUECHUA_PERU 0x0c6b
#define TT_MS_LANGID_SEPEDI_SOUTH_AFRICA 0x046c #define TT_MS_LANGID_SEPEDI_SOUTH_AFRICA 0x046c
/* Also spelled by XPsp2 Platform SDK (2004-07-26) */ /* Also spelled by XP_sp2 Platform SDK (2004-07-26) */
#define TT_MS_LANGID_SOTHO_SOUTHERN_SOUTH_AFRICA \ #define TT_MS_LANGID_SOTHO_SOUTHERN_SOUTH_AFRICA \
TT_MS_LANGID_SEPEDI_SOUTH_AFRICA TT_MS_LANGID_SEPEDI_SOUTH_AFRICA
/* language codes 0x046d, 0x046e and 0x046f are (still) unknown. */ /* language codes 0x046d, 0x046e and 0x046f are (still) unknown. */

View File

@ -1,5 +0,0 @@
#include <math.h>
long long int lrintf(float x) {
return floor(x);
}

View File

@ -1,28 +0,0 @@
#include <ctype.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <inttypes.h>
long long int strtoll(const char *nptr, char **endptr, int base)
{
int neg=0;
unsigned long long int v;
const char*orig=nptr;
while(isspace(*nptr)) nptr++;
if (*nptr == '-' && isalnum(nptr[1])) { neg=-1; nptr++; }
v=strtoull(nptr,endptr,base);
if (endptr && *endptr==nptr) *endptr=(char *)orig;
if (v>LLONG_MAX) {
if (v==0x8000000000000000ull && neg) {
errno=0;
return v;
}
errno=ERANGE;
return (neg?LLONG_MIN:LLONG_MAX);
}
return (neg?-v:v);
}

View File

@ -1,233 +0,0 @@
#ifndef _MUXPS_H_
#define _MUXPS_H_
#ifndef _FITZ_H_
#error "fitz.h must be included before muxps.h"
#endif
typedef unsigned char byte;
/*
* XPS and ZIP constants.
*/
typedef struct xps_context_s xps_context;
#define REL_START_PART \
"http://schemas.microsoft.com/xps/2005/06/fixedrepresentation"
#define REL_REQUIRED_RESOURCE \
"http://schemas.microsoft.com/xps/2005/06/required-resource"
#define REL_REQUIRED_RESOURCE_RECURSIVE \
"http://schemas.microsoft.com/xps/2005/06/required-resource#recursive"
#define ZIP_LOCAL_FILE_SIG 0x04034b50
#define ZIP_DATA_DESC_SIG 0x08074b50
#define ZIP_CENTRAL_DIRECTORY_SIG 0x02014b50
#define ZIP_END_OF_CENTRAL_DIRECTORY_SIG 0x06054b50
/*
* Memory, and string functions.
*/
int xps_strcasecmp(char *a, char *b);
void xps_absolute_path(char *output, char *base_uri, char *path, int output_size);
/*
* XML document model
*/
typedef struct element xml_element;
xml_element *xml_parse_document(byte *buf, int len);
xml_element *xml_next(xml_element *item);
xml_element *xml_down(xml_element *item);
char *xml_tag(xml_element *item);
char *xml_att(xml_element *item, const char *att);
void xml_free_element(xml_element *item);
void xml_print_element(xml_element *item, int level);
/*
* Container parts.
*/
typedef struct xps_part_s xps_part;
struct xps_part_s
{
char *name;
int size;
int cap;
byte *data;
};
xps_part *xps_new_part(xps_context *ctx, char *name, int size);
xps_part *xps_read_part(xps_context *ctx, char *partname);
void xps_free_part(xps_context *ctx, xps_part *part);
/*
* Document structure.
*/
typedef struct xps_document_s xps_document;
typedef struct xps_page_s xps_page;
struct xps_document_s
{
char *name;
xps_document *next;
};
struct xps_page_s
{
char *name;
int width;
int height;
xml_element *root;
xps_page *next;
};
int xps_read_page_list(xps_context *ctx);
void xps_debug_page_list(xps_context *ctx);
void xps_free_page_list(xps_context *ctx);
int xps_count_pages(xps_context *ctx);
int xps_load_page(xps_page **page, xps_context *ctx, int number);
void xps_free_page(xps_context *ctx, xps_page *page);
/*
* Images, fonts, and colorspaces.
*/
int xps_decode_jpeg(fz_pixmap **imagep, byte *rbuf, int rlen);
int xps_decode_png(fz_pixmap **imagep, byte *rbuf, int rlen);
int xps_decode_tiff(fz_pixmap **imagep, byte *rbuf, int rlen);
typedef struct xps_font_cache_s xps_font_cache;
struct xps_font_cache_s
{
char *name;
fz_font *font;
xps_font_cache *next;
};
typedef struct xps_glyph_metrics_s xps_glyph_metrics;
struct xps_glyph_metrics_s
{
float hadv, vadv, vorg;
};
int xps_count_font_encodings(fz_font *font);
void xps_identify_font_encoding(fz_font *font, int idx, int *pid, int *eid);
void xps_select_font_encoding(fz_font *font, int idx);
int xps_encode_font_char(fz_font *font, int key);
void xps_measure_font_glyph(xps_context *ctx, fz_font *font, int gid, xps_glyph_metrics *mtx);
void xps_debug_path(xps_context *ctx);
void xps_parse_color(xps_context *ctx, char *base_uri, char *hexstring, fz_colorspace **csp, float *samples);
void xps_set_color(xps_context *ctx, fz_colorspace *colorspace, float *samples);
/*
* Resource dictionaries.
*/
typedef struct xps_resource_s xps_resource;
struct xps_resource_s
{
char *name;
char *base_uri; /* only used in the head nodes */
xml_element *base_xml; /* only used in the head nodes, to free the xml document */
xml_element *data;
xps_resource *next;
xps_resource *parent; /* up to the previous dict in the stack */
};
int xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base_uri, xml_element *root);
void xps_free_resource_dictionary(xps_context *ctx, xps_resource *dict);
void xps_resolve_resource_reference(xps_context *ctx, xps_resource *dict, char **attp, xml_element **tagp, char **urip);
void xps_debug_resource_dictionary(xps_resource *dict);
/*
* Fixed page/graphics parsing.
*/
void xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page);
void xps_parse_canvas(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node);
void xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node);
void xps_parse_glyphs(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node);
void xps_parse_solid_color_brush(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *node);
void xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node);
void xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node);
void xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node);
void xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node);
void xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *root, void(*func)(xps_context*, fz_matrix, fz_rect, char*, xps_resource*, xml_element*, void*), void *user);
void xps_parse_matrix_transform(xps_context *ctx, xml_element *root, fz_matrix *matrix);
void xps_parse_render_transform(xps_context *ctx, char *text, fz_matrix *matrix);
void xps_parse_rectangle(xps_context *ctx, char *text, fz_rect *rect);
void xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, char *opacity_att, xml_element *opacity_mask_tag);
void xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict, char *opacity_att, xml_element *opacity_mask_tag);
void xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node);
void xps_parse_element(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node);
void xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xml_element *clip_tag);
/*
* The interpreter context.
*/
typedef struct xps_entry_s xps_entry;
struct xps_entry_s
{
char *name;
int offset;
int csize;
int usize;
};
struct xps_context_s
{
char *directory;
fz_stream *file;
int zip_count;
xps_entry *zip_table;
char *start_part; /* fixed document sequence */
xps_document *first_fixdoc; /* first fixed document */
xps_document *last_fixdoc; /* last fixed document */
xps_page *first_page; /* first page of document */
xps_page *last_page; /* last page of document */
char *base_uri; /* base uri for parsing XML and resolving relative paths */
char *part_uri; /* part uri for parsing metadata relations */
/* We cache font resources */
xps_font_cache *font_table;
/* Opacity attribute stack */
float opacity[64];
int opacity_top;
/* Current color */
fz_colorspace *colorspace;
float color[8];
float alpha;
/* Current device */
fz_device *dev;
};
int xps_open_file(xps_context **ctxp, char *filename);
int xps_open_stream(xps_context **ctxp, fz_stream *file);
void xps_free_context(xps_context *ctx);
#endif

View File

@ -1,282 +0,0 @@
#include "fitz.h"
#include "muxps.h"
static inline int unhex(int a)
{
if (a >= 'A' && a <= 'F') return a - 'A' + 0xA;
if (a >= 'a' && a <= 'f') return a - 'a' + 0xA;
if (a >= '0' && a <= '9') return a - '0';
return 0;
}
void
xps_parse_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node)
{
/* SolidColorBrushes are handled in a special case and will never show up here */
if (!strcmp(xml_tag(node), "ImageBrush"))
xps_parse_image_brush(ctx, ctm, area, base_uri, dict, node);
else if (!strcmp(xml_tag(node), "VisualBrush"))
xps_parse_visual_brush(ctx, ctm, area, base_uri, dict, node);
else if (!strcmp(xml_tag(node), "LinearGradientBrush"))
xps_parse_linear_gradient_brush(ctx, ctm, area, base_uri, dict, node);
else if (!strcmp(xml_tag(node), "RadialGradientBrush"))
xps_parse_radial_gradient_brush(ctx, ctm, area, base_uri, dict, node);
else
fz_warn("unknown brush tag: %s", xml_tag(node));
}
void
xps_parse_element(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *node)
{
if (!strcmp(xml_tag(node), "Path"))
xps_parse_path(ctx, ctm, base_uri, dict, node);
if (!strcmp(xml_tag(node), "Glyphs"))
xps_parse_glyphs(ctx, ctm, base_uri, dict, node);
if (!strcmp(xml_tag(node), "Canvas"))
xps_parse_canvas(ctx, ctm, area, base_uri, dict, node);
/* skip unknown tags (like Foo.Resources and similar) */
}
void
xps_begin_opacity(xps_context *ctx, fz_matrix ctm, fz_rect area,
char *base_uri, xps_resource *dict,
char *opacity_att, xml_element *opacity_mask_tag)
{
float opacity;
if (!opacity_att && !opacity_mask_tag)
return;
opacity = 1;
if (opacity_att)
opacity = fz_atof(opacity_att);
if (opacity_mask_tag && !strcmp(xml_tag(opacity_mask_tag), "SolidColorBrush"))
{
char *scb_opacity_att = xml_att(opacity_mask_tag, "Opacity");
char *scb_color_att = xml_att(opacity_mask_tag, "Color");
if (scb_opacity_att)
opacity = opacity * fz_atof(scb_opacity_att);
if (scb_color_att)
{
fz_colorspace *colorspace;
float samples[32];
xps_parse_color(ctx, base_uri, scb_color_att, &colorspace, samples);
opacity = opacity * samples[0];
}
opacity_mask_tag = NULL;
}
if (ctx->opacity_top + 1 < nelem(ctx->opacity))
{
ctx->opacity[ctx->opacity_top + 1] = ctx->opacity[ctx->opacity_top] * opacity;
ctx->opacity_top++;
}
if (opacity_mask_tag)
{
fz_begin_mask(ctx->dev, area, 0, NULL, NULL);
xps_parse_brush(ctx, ctm, area, base_uri, dict, opacity_mask_tag);
fz_end_mask(ctx->dev);
}
}
void
xps_end_opacity(xps_context *ctx, char *base_uri, xps_resource *dict,
char *opacity_att, xml_element *opacity_mask_tag)
{
if (!opacity_att && !opacity_mask_tag)
return;
if (ctx->opacity_top > 0)
ctx->opacity_top--;
if (opacity_mask_tag)
{
if (strcmp(xml_tag(opacity_mask_tag), "SolidColorBrush"))
fz_pop_clip(ctx->dev);
}
}
void
xps_parse_render_transform(xps_context *ctx, char *transform, fz_matrix *matrix)
{
float args[6];
char *s = transform;
int i;
args[0] = 1; args[1] = 0;
args[2] = 0; args[3] = 1;
args[4] = 0; args[5] = 0;
for (i = 0; i < 6 && *s; i++)
{
args[i] = fz_atof(s);
while (*s && *s != ',')
s++;
if (*s == ',')
s++;
}
matrix->a = args[0]; matrix->b = args[1];
matrix->c = args[2]; matrix->d = args[3];
matrix->e = args[4]; matrix->f = args[5];
}
void
xps_parse_matrix_transform(xps_context *ctx, xml_element *root, fz_matrix *matrix)
{
char *transform;
*matrix = fz_identity;
if (!strcmp(xml_tag(root), "MatrixTransform"))
{
transform = xml_att(root, "Matrix");
if (transform)
xps_parse_render_transform(ctx, transform, matrix);
}
}
void
xps_parse_rectangle(xps_context *ctx, char *text, fz_rect *rect)
{
float args[4];
char *s = text;
int i;
args[0] = 0; args[1] = 0;
args[2] = 1; args[3] = 1;
for (i = 0; i < 4 && *s; i++)
{
args[i] = fz_atof(s);
while (*s && *s != ',')
s++;
if (*s == ',')
s++;
}
rect->x0 = args[0];
rect->y0 = args[1];
rect->x1 = args[0] + args[2];
rect->y1 = args[1] + args[3];
}
static int count_commas(char *s)
{
int n = 0;
while (*s)
{
if (*s == ',')
n ++;
s ++;
}
return n;
}
void
xps_parse_color(xps_context *ctx, char *base_uri, char *string,
fz_colorspace **csp, float *samples)
{
char *p;
int i, n;
char buf[1024];
char *profile;
*csp = fz_device_rgb;
samples[0] = 1;
samples[1] = 0;
samples[2] = 0;
samples[3] = 0;
if (string[0] == '#')
{
if (strlen(string) == 9)
{
samples[0] = unhex(string[1]) * 16 + unhex(string[2]);
samples[1] = unhex(string[3]) * 16 + unhex(string[4]);
samples[2] = unhex(string[5]) * 16 + unhex(string[6]);
samples[3] = unhex(string[7]) * 16 + unhex(string[8]);
}
else
{
samples[0] = 255;
samples[1] = unhex(string[1]) * 16 + unhex(string[2]);
samples[2] = unhex(string[3]) * 16 + unhex(string[4]);
samples[3] = unhex(string[5]) * 16 + unhex(string[6]);
}
samples[0] /= 255;
samples[1] /= 255;
samples[2] /= 255;
samples[3] /= 255;
}
else if (string[0] == 's' && string[1] == 'c' && string[2] == '#')
{
if (count_commas(string) == 2)
sscanf(string, "sc#%g,%g,%g", samples + 1, samples + 2, samples + 3);
if (count_commas(string) == 3)
sscanf(string, "sc#%g,%g,%g,%g", samples, samples + 1, samples + 2, samples + 3);
}
else if (strstr(string, "ContextColor ") == string)
{
/* Crack the string for profile name and sample values */
fz_strlcpy(buf, string, sizeof buf);
profile = strchr(buf, ' ');
if (!profile)
{
fz_warn("cannot find icc profile uri in '%s'", string);
return;
}
*profile++ = 0;
p = strchr(profile, ' ');
if (!p)
{
fz_warn("cannot find component values in '%s'", profile);
return;
}
*p++ = 0;
n = count_commas(p) + 1;
i = 0;
while (i < n)
{
samples[i++] = fz_atof(p);
p = strchr(p, ',');
if (!p)
break;
p ++;
if (*p == ' ')
p ++;
}
while (i < n)
{
samples[i++] = 0;
}
/* TODO: load ICC profile */
switch (n)
{
case 2: *csp = fz_device_gray; break;
case 4: *csp = fz_device_rgb; break;
case 5: *csp = fz_device_cmyk; break;
default: *csp = fz_device_gray; break;
}
}
}
void
xps_set_color(xps_context *ctx, fz_colorspace *colorspace, float *samples)
{
int i;
ctx->colorspace = colorspace;
for (i = 0; i < colorspace->n; i++)
ctx->color[i] = samples[i + 1];
ctx->alpha = samples[0] * ctx->opacity[ctx->opacity_top];
}

View File

@ -1,341 +0,0 @@
#include "fitz.h"
#include "muxps.h"
/*
* The FixedDocumentSequence and FixedDocument parts determine
* which parts correspond to actual pages, and the page order.
*/
void
xps_debug_page_list(xps_context *ctx)
{
xps_document *fixdoc = ctx->first_fixdoc;
xps_page *page = ctx->first_page;
if (ctx->start_part)
printf("start part %s\n", ctx->start_part);
while (fixdoc)
{
printf("fixdoc %s\n", fixdoc->name);
fixdoc = fixdoc->next;
}
while (page)
{
printf("page %s w=%d h=%d\n", page->name, page->width, page->height);
page = page->next;
}
}
static void
xps_add_fixed_document(xps_context *ctx, char *name)
{
xps_document *fixdoc;
/* Check for duplicates first */
for (fixdoc = ctx->first_fixdoc; fixdoc; fixdoc = fixdoc->next)
if (!strcmp(fixdoc->name, name))
return;
fixdoc = fz_malloc(sizeof(xps_document));
fixdoc->name = fz_strdup(name);
fixdoc->next = NULL;
if (!ctx->first_fixdoc)
{
ctx->first_fixdoc = fixdoc;
ctx->last_fixdoc = fixdoc;
}
else
{
ctx->last_fixdoc->next = fixdoc;
ctx->last_fixdoc = fixdoc;
}
}
static void
xps_add_fixed_page(xps_context *ctx, char *name, int width, int height)
{
xps_page *page;
/* Check for duplicates first */
for (page = ctx->first_page; page; page = page->next)
if (!strcmp(page->name, name))
return;
page = fz_malloc(sizeof(xps_page));
page->name = fz_strdup(name);
page->width = width;
page->height = height;
page->root = NULL;
page->next = NULL;
if (!ctx->first_page)
{
ctx->first_page = page;
ctx->last_page = page;
}
else
{
ctx->last_page->next = page;
ctx->last_page = page;
}
}
static void
xps_free_fixed_pages(xps_context *ctx)
{
xps_page *page = ctx->first_page;
while (page)
{
xps_page *next = page->next;
xps_free_page(ctx, page);
fz_free(page->name);
fz_free(page);
page = next;
}
ctx->first_page = NULL;
ctx->last_page = NULL;
}
static void
xps_free_fixed_documents(xps_context *ctx)
{
xps_document *doc = ctx->first_fixdoc;
while (doc)
{
xps_document *next = doc->next;
fz_free(doc->name);
fz_free(doc);
doc = next;
}
ctx->first_fixdoc = NULL;
ctx->last_fixdoc = NULL;
}
void
xps_free_page_list(xps_context *ctx)
{
xps_free_fixed_documents(ctx);
xps_free_fixed_pages(ctx);
}
/*
* Parse the fixed document sequence structure and _rels/.rels to find the start part.
*/
static void
xps_parse_metadata_imp(xps_context *ctx, xml_element *item)
{
while (item)
{
xps_parse_metadata_imp(ctx, xml_down(item));
if (!strcmp(xml_tag(item), "Relationship"))
{
char *target = xml_att(item, "Target");
char *type = xml_att(item, "Type");
if (target && type)
{
char tgtbuf[1024];
xps_absolute_path(tgtbuf, ctx->base_uri, target, sizeof tgtbuf);
if (!strcmp(type, REL_START_PART))
ctx->start_part = fz_strdup(tgtbuf);
}
}
if (!strcmp(xml_tag(item), "DocumentReference"))
{
char *source = xml_att(item, "Source");
if (source)
{
char srcbuf[1024];
xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf);
xps_add_fixed_document(ctx, srcbuf);
}
}
if (!strcmp(xml_tag(item), "PageContent"))
{
char *source = xml_att(item, "Source");
char *width_att = xml_att(item, "Width");
char *height_att = xml_att(item, "Height");
int width = width_att ? atoi(width_att) : 0;
int height = height_att ? atoi(height_att) : 0;
if (source)
{
char srcbuf[1024];
xps_absolute_path(srcbuf, ctx->base_uri, source, sizeof srcbuf);
xps_add_fixed_page(ctx, srcbuf, width, height);
}
}
item = xml_next(item);
}
}
static int
xps_parse_metadata(xps_context *ctx, xps_part *part)
{
xml_element *root;
char buf[1024];
char *s;
/* Save directory name part */
fz_strlcpy(buf, part->name, sizeof buf);
s = strrchr(buf, '/');
if (s)
s[0] = 0;
/* _rels parts are voodoo: their URI references are from
* the part they are associated with, not the actual _rels
* part being parsed.
*/
s = strstr(buf, "/_rels");
if (s)
*s = 0;
ctx->base_uri = buf;
ctx->part_uri = part->name;
root = xml_parse_document(part->data, part->size);
if (!root)
return fz_rethrow(-1, "cannot parse metadata part '%s'", part->name);
xps_parse_metadata_imp(ctx, root);
xml_free_element(root);
ctx->base_uri = NULL;
ctx->part_uri = NULL;
return fz_okay;
}
static int
xps_read_and_process_metadata_part(xps_context *ctx, char *name)
{
xps_part *part;
int code;
part = xps_read_part(ctx, name);
if (!part)
return fz_rethrow(-1, "cannot read zip part '%s'", name);
code = xps_parse_metadata(ctx, part);
if (code)
return fz_rethrow(code, "cannot process metadata part '%s'", name);
xps_free_part(ctx, part);
return fz_okay;
}
int
xps_read_page_list(xps_context *ctx)
{
xps_document *doc;
int code;
code = xps_read_and_process_metadata_part(ctx, "/_rels/.rels");
if (code)
return fz_rethrow(code, "cannot process root relationship part");
if (!ctx->start_part)
return fz_throw("cannot find fixed document sequence start part");
code = xps_read_and_process_metadata_part(ctx, ctx->start_part);
if (code)
return fz_rethrow(code, "cannot process FixedDocumentSequence part");
for (doc = ctx->first_fixdoc; doc; doc = doc->next)
{
code = xps_read_and_process_metadata_part(ctx, doc->name);
if (code)
return fz_rethrow(code, "cannot process FixedDocument part");
}
return fz_okay;
}
int
xps_count_pages(xps_context *ctx)
{
xps_page *page;
int n = 0;
for (page = ctx->first_page; page; page = page->next)
n ++;
return n;
}
static int
xps_load_fixed_page(xps_context *ctx, xps_page *page)
{
xps_part *part;
xml_element *root;
char *width_att;
char *height_att;
part = xps_read_part(ctx, page->name);
if (!part)
return fz_rethrow(-1, "cannot read zip part '%s'", page->name);
root = xml_parse_document(part->data, part->size);
if (!root)
return fz_rethrow(-1, "cannot parse xml part '%s'", page->name);
xps_free_part(ctx, part);
if (strcmp(xml_tag(root), "FixedPage"))
return fz_throw("expected FixedPage element (found %s)", xml_tag(root));
width_att = xml_att(root, "Width");
if (!width_att)
return fz_throw("FixedPage missing required attribute: Width");
height_att = xml_att(root, "Height");
if (!height_att)
return fz_throw("FixedPage missing required attribute: Height");
page->width = atoi(width_att);
page->height = atoi(height_att);
page->root = root;
return 0;
}
int
xps_load_page(xps_page **pagep, xps_context *ctx, int number)
{
xps_page *page;
int code;
int n = 0;
for (page = ctx->first_page; page; page = page->next)
{
if (n == number)
{
if (!page->root)
{
code = xps_load_fixed_page(ctx, page);
if (code)
return fz_rethrow(code, "cannot load page %d", number + 1);
}
*pagep = page;
return fz_okay;
}
n ++;
}
return fz_throw("cannot find page %d", number + 1);
}
void
xps_free_page(xps_context *ctx, xps_page *page)
{
/* only free the XML contents */
if (page->root)
xml_free_element(page->root);
page->root = NULL;
}

View File

@ -1,584 +0,0 @@
#include "fitz.h"
#include "muxps.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_ADVANCES_H
static inline int ishex(int a)
{
return (a >= 'A' && a <= 'F') ||
(a >= 'a' && a <= 'f') ||
(a >= '0' && a <= '9');
}
static inline int unhex(int a)
{
if (a >= 'A' && a <= 'F') return a - 'A' + 0xA;
if (a >= 'a' && a <= 'f') return a - 'a' + 0xA;
if (a >= '0' && a <= '9') return a - '0';
return 0;
}
int
xps_count_font_encodings(fz_font *font)
{
FT_Face face = font->ft_face;
return face->num_charmaps;
}
void
xps_identify_font_encoding(fz_font *font, int idx, int *pid, int *eid)
{
FT_Face face = font->ft_face;
*pid = face->charmaps[idx]->platform_id;
*eid = face->charmaps[idx]->encoding_id;
}
void
xps_select_font_encoding(fz_font *font, int idx)
{
FT_Face face = font->ft_face;
FT_Set_Charmap(face, face->charmaps[idx]);
}
int
xps_encode_font_char(fz_font *font, int code)
{
FT_Face face = font->ft_face;
int gid = FT_Get_Char_Index(face, code);
if (gid == 0 && face->charmap->platform_id == 3 && face->charmap->encoding_id == 0)
gid = FT_Get_Char_Index(face, 0xF000 | code);
return gid;
}
void
xps_measure_font_glyph(xps_context *ctx, fz_font *font, int gid, xps_glyph_metrics *mtx)
{
int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM;
FT_Face face = font->ft_face;
FT_Fixed hadv, vadv;
FT_Set_Char_Size(face, 64, 64, 72, 72);
FT_Get_Advance(face, gid, mask, &hadv);
FT_Get_Advance(face, gid, mask | FT_LOAD_VERTICAL_LAYOUT, &vadv);
mtx->hadv = hadv / 65536.0f;
mtx->vadv = vadv / 65536.0f;
mtx->vorg = face->ascender / (float) face->units_per_EM;
}
static fz_font *
xps_lookup_font(xps_context *ctx, char *name)
{
xps_font_cache *cache;
for (cache = ctx->font_table; cache; cache = cache->next)
if (!xps_strcasecmp(cache->name, name))
return fz_keep_font(cache->font);
return NULL;
}
static void
xps_insert_font(xps_context *ctx, char *name, fz_font *font)
{
xps_font_cache *cache = fz_malloc(sizeof(xps_font_cache));
cache->name = fz_strdup(name);
cache->font = fz_keep_font(font);
cache->next = ctx->font_table;
ctx->font_table = cache;
}
/*
* Some fonts in XPS are obfuscated by XOR:ing the first 32 bytes of the
* data with the GUID in the fontname.
*/
static void
xps_deobfuscate_font_resource(xps_context *ctx, xps_part *part)
{
byte buf[33];
byte key[16];
char *p;
int i;
p = strrchr(part->name, '/');
if (!p)
p = part->name;
for (i = 0; i < 32 && *p; p++)
{
if (ishex(*p))
buf[i++] = *p;
}
buf[i] = 0;
if (i != 32)
{
fz_warn("cannot extract GUID from obfuscated font part name");
return;
}
for (i = 0; i < 16; i++)
key[i] = unhex(buf[i*2+0]) * 16 + unhex(buf[i*2+1]);
for (i = 0; i < 16; i++)
{
part->data[i] ^= key[15-i];
part->data[i+16] ^= key[15-i];
}
}
static void
xps_select_best_font_encoding(fz_font *font)
{
static struct { int pid, eid; } xps_cmap_list[] =
{
{ 3, 10 }, /* Unicode with surrogates */
{ 3, 1 }, /* Unicode without surrogates */
{ 3, 5 }, /* Wansung */
{ 3, 4 }, /* Big5 */
{ 3, 3 }, /* Prc */
{ 3, 2 }, /* ShiftJis */
{ 3, 0 }, /* Symbol */
// { 0, * }, -- Unicode (deprecated)
{ 1, 0 },
{ -1, -1 },
};
int i, k, n, pid, eid;
n = xps_count_font_encodings(font);
for (k = 0; xps_cmap_list[k].pid != -1; k++)
{
for (i = 0; i < n; i++)
{
xps_identify_font_encoding(font, i, &pid, &eid);
if (pid == xps_cmap_list[k].pid && eid == xps_cmap_list[k].eid)
{
xps_select_font_encoding(font, i);
return;
}
}
}
fz_warn("cannot find a suitable cmap");
}
/*
* Parse and draw an XPS <Glyphs> element.
*
* Indices syntax:
GlyphIndices = GlyphMapping ( ";" GlyphMapping )
GlyphMapping = ( [ClusterMapping] GlyphIndex ) [GlyphMetrics]
ClusterMapping = "(" ClusterCodeUnitCount [":" ClusterGlyphCount] ")"
ClusterCodeUnitCount = * DIGIT
ClusterGlyphCount = * DIGIT
GlyphIndex = * DIGIT
GlyphMetrics = "," AdvanceWidth ["," uOffset ["," vOffset]]
AdvanceWidth = ["+"] RealNum
uOffset = ["+" | "-"] RealNum
vOffset = ["+" | "-"] RealNum
RealNum = ((DIGIT ["." DIGIT]) | ("." DIGIT)) [Exponent]
Exponent = ( ("E"|"e") ("+"|"-") DIGIT )
*/
static char *
xps_parse_digits(char *s, int *digit)
{
*digit = 0;
while (*s >= '0' && *s <= '9')
{
*digit = *digit * 10 + (*s - '0');
s ++;
}
return s;
}
static inline int is_real_num_char(int c)
{
return (c >= '0' && c <= '9') ||
c == 'e' || c == 'E' || c == '+' || c == '-' || c == '.';
}
static char *
xps_parse_real_num(char *s, float *number)
{
char buf[64];
char *p = buf;
while (is_real_num_char(*s))
*p++ = *s++;
*p = 0;
if (buf[0])
*number = fz_atof(buf);
return s;
}
static char *
xps_parse_cluster_mapping(char *s, int *code_count, int *glyph_count)
{
if (*s == '(')
s = xps_parse_digits(s + 1, code_count);
if (*s == ':')
s = xps_parse_digits(s + 1, glyph_count);
if (*s == ')')
s ++;
return s;
}
static char *
xps_parse_glyph_index(char *s, int *glyph_index)
{
if (*s >= '0' && *s <= '9')
s = xps_parse_digits(s, glyph_index);
return s;
}
static char *
xps_parse_glyph_metrics(char *s, float *advance, float *uofs, float *vofs)
{
if (*s == ',')
s = xps_parse_real_num(s + 1, advance);
if (*s == ',')
s = xps_parse_real_num(s + 1, uofs);
if (*s == ',')
s = xps_parse_real_num(s + 1, vofs);
return s;
}
/*
* Parse unicode and indices strings and encode glyphs.
* Calculate metrics for positioning.
*/
static fz_text *
xps_parse_glyphs_imp(xps_context *ctx, fz_matrix ctm,
fz_font *font, float size, float originx, float originy,
int is_sideways, int bidi_level,
char *indices, char *unicode)
{
xps_glyph_metrics mtx;
fz_text *text;
fz_matrix tm;
float e, f;
float x = originx;
float y = originy;
char *us = unicode;
char *is = indices;
int un = 0;
if (!unicode && !indices)
fz_warn("glyphs element with neither characters nor indices");
if (us)
{
if (us[0] == '{' && us[1] == '}')
us = us + 2;
un = strlen(us);
}
if (is_sideways)
tm = fz_concat(fz_scale(-size, size), fz_rotate(90));
else
tm = fz_scale(size, -size);
text = fz_new_text(font, tm, is_sideways);
while ((us && un > 0) || (is && *is))
{
int char_code = '?';
int code_count = 1;
int glyph_count = 1;
if (is && *is)
{
is = xps_parse_cluster_mapping(is, &code_count, &glyph_count);
}
if (code_count < 1)
code_count = 1;
if (glyph_count < 1)
glyph_count = 1;
/* TODO: add code chars with cluster mappings for text extraction */
while (code_count--)
{
if (us && un > 0)
{
int t = chartorune(&char_code, us);
us += t; un -= t;
}
}
while (glyph_count--)
{
int glyph_index = -1;
float u_offset = 0;
float v_offset = 0;
float advance;
if (is && *is)
is = xps_parse_glyph_index(is, &glyph_index);
if (glyph_index == -1)
glyph_index = xps_encode_font_char(font, char_code);
xps_measure_font_glyph(ctx, font, glyph_index, &mtx);
if (is_sideways)
advance = mtx.vadv * 100;
else if (bidi_level & 1)
advance = -mtx.hadv * 100;
else
advance = mtx.hadv * 100;
if (is && *is)
{
is = xps_parse_glyph_metrics(is, &advance, &u_offset, &v_offset);
if (*is == ';')
is ++;
}
if (bidi_level & 1)
u_offset = -mtx.hadv * 100 - u_offset;
u_offset = u_offset * 0.01f * size;
v_offset = v_offset * 0.01f * size;
if (is_sideways)
{
e = x + u_offset + (mtx.vorg * size);
f = y - v_offset + (mtx.hadv * 0.5f * size);
}
else
{
e = x + u_offset;
f = y - v_offset;
}
fz_add_text(text, glyph_index, char_code, e, f);
x += advance * 0.01f * size;
}
}
return text;
}
void
xps_parse_glyphs(xps_context *ctx, fz_matrix ctm,
char *base_uri, xps_resource *dict, xml_element *root)
{
xml_element *node;
int code;
char *fill_uri;
char *opacity_mask_uri;
char *bidi_level_att;
char *caret_stops_att;
char *fill_att;
char *font_size_att;
char *font_uri_att;
char *origin_x_att;
char *origin_y_att;
char *is_sideways_att;
char *indices_att;
char *unicode_att;
char *style_att;
char *transform_att;
char *clip_att;
char *opacity_att;
char *opacity_mask_att;
xml_element *transform_tag = NULL;
xml_element *clip_tag = NULL;
xml_element *fill_tag = NULL;
xml_element *opacity_mask_tag = NULL;
char *fill_opacity_att = NULL;
xps_part *part;
fz_font *font;
char partname[1024];
char *subfont;
float font_size = 10;
int subfontid = 0;
int is_sideways = 0;
int bidi_level = 0;
fz_text *text;
fz_rect area;
/*
* Extract attributes and extended attributes.
*/
bidi_level_att = xml_att(root, "BidiLevel");
caret_stops_att = xml_att(root, "CaretStops");
fill_att = xml_att(root, "Fill");
font_size_att = xml_att(root, "FontRenderingEmSize");
font_uri_att = xml_att(root, "FontUri");
origin_x_att = xml_att(root, "OriginX");
origin_y_att = xml_att(root, "OriginY");
is_sideways_att = xml_att(root, "IsSideways");
indices_att = xml_att(root, "Indices");
unicode_att = xml_att(root, "UnicodeString");
style_att = xml_att(root, "StyleSimulations");
transform_att = xml_att(root, "RenderTransform");
clip_att = xml_att(root, "Clip");
opacity_att = xml_att(root, "Opacity");
opacity_mask_att = xml_att(root, "OpacityMask");
for (node = xml_down(root); node; node = xml_next(node))
{
if (!strcmp(xml_tag(node), "Glyphs.RenderTransform"))
transform_tag = xml_down(node);
if (!strcmp(xml_tag(node), "Glyphs.OpacityMask"))
opacity_mask_tag = xml_down(node);
if (!strcmp(xml_tag(node), "Glyphs.Clip"))
clip_tag = xml_down(node);
if (!strcmp(xml_tag(node), "Glyphs.Fill"))
fill_tag = xml_down(node);
}
fill_uri = base_uri;
opacity_mask_uri = base_uri;
xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL);
xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL);
xps_resolve_resource_reference(ctx, dict, &fill_att, &fill_tag, &fill_uri);
xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);
/*
* Check that we have all the necessary information.
*/
if (!font_size_att || !font_uri_att || !origin_x_att || !origin_y_att) {
fz_warn("missing attributes in glyphs element");
return;
}
if (!indices_att && !unicode_att)
return; /* nothing to draw */
if (is_sideways_att)
is_sideways = !strcmp(is_sideways_att, "true");
if (bidi_level_att)
bidi_level = atoi(bidi_level_att);
/*
* Find and load the font resource
*/
xps_absolute_path(partname, base_uri, font_uri_att, sizeof partname);
subfont = strrchr(partname, '#');
if (subfont)
{
subfontid = atoi(subfont + 1);
*subfont = 0;
}
font = xps_lookup_font(ctx, partname);
if (!font)
{
part = xps_read_part(ctx, partname);
if (!part) {
fz_warn("cannot find font resource part '%s'", partname);
return;
}
/* deobfuscate if necessary */
if (strstr(part->name, ".odttf"))
xps_deobfuscate_font_resource(ctx, part);
if (strstr(part->name, ".ODTTF"))
xps_deobfuscate_font_resource(ctx, part);
code = fz_new_font_from_memory(&font, part->data, part->size, subfontid);
if (code) {
fz_catch(code, "cannot load font resource '%s'", partname);
xps_free_part(ctx, part);
return;
}
xps_select_best_font_encoding(font);
xps_insert_font(ctx, part->name, font);
/* NOTE: we keep part->data in the font */
font->ft_data = part->data;
font->ft_size = part->size;
fz_free(part->name);
fz_free(part);
}
/*
* Set up graphics state.
*/
if (transform_att || transform_tag)
{
fz_matrix transform;
if (transform_att)
xps_parse_render_transform(ctx, transform_att, &transform);
if (transform_tag)
xps_parse_matrix_transform(ctx, transform_tag, &transform);
ctm = fz_concat(transform, ctm);
}
if (clip_att || clip_tag)
xps_clip(ctx, ctm, dict, clip_att, clip_tag);
font_size = fz_atof(font_size_att);
text = xps_parse_glyphs_imp(ctx, ctm, font, font_size,
fz_atof(origin_x_att), fz_atof(origin_y_att),
is_sideways, bidi_level, indices_att, unicode_att);
area = fz_bound_text(text, ctm);
xps_begin_opacity(ctx, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
/* If it's a solid color brush fill/stroke do a simple fill */
if (fill_tag && !strcmp(xml_tag(fill_tag), "SolidColorBrush"))
{
fill_opacity_att = xml_att(fill_tag, "Opacity");
fill_att = xml_att(fill_tag, "Color");
fill_tag = NULL;
}
if (fill_att)
{
float samples[32];
fz_colorspace *colorspace;
xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples);
if (fill_opacity_att)
samples[0] = fz_atof(fill_opacity_att);
xps_set_color(ctx, colorspace, samples);
fz_fill_text(ctx->dev, text, ctm,
ctx->colorspace, ctx->color, ctx->alpha);
}
/* If it's a complex brush, use the charpath as a clip mask */
if (fill_tag)
{
fz_clip_text(ctx->dev, text, ctm, 0);
xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag);
fz_pop_clip(ctx->dev);
}
xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
fz_free_text(text);
if (clip_att || clip_tag)
fz_pop_clip(ctx->dev);
fz_drop_font(font);
}

View File

@ -1,458 +0,0 @@
#include "fitz.h"
#include "muxps.h"
#define MAX_STOPS 256
enum { SPREAD_PAD, SPREAD_REPEAT, SPREAD_REFLECT };
/*
* Parse a list of GradientStop elements.
* Fill the offset and color arrays, and
* return the number of stops parsed.
*/
struct stop
{
float offset;
float r, g, b, a;
};
static int cmp_stop(const void *a, const void *b)
{
const struct stop *astop = a;
const struct stop *bstop = b;
float diff = astop->offset - bstop->offset;
if (diff < 0)
return -1;
if (diff > 0)
return 1;
return 0;
}
static inline float lerp(float a, float b, float x)
{
return a + (b - a) * x;
}
static int
xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xml_element *node,
struct stop *stops, int maxcount)
{
fz_colorspace *colorspace;
float sample[8];
float rgb[3];
int before, after;
int count;
int i;
/* We may have to insert 2 extra stops when postprocessing */
maxcount -= 2;
count = 0;
while (node && count < maxcount)
{
if (!strcmp(xml_tag(node), "GradientStop"))
{
char *offset = xml_att(node, "Offset");
char *color = xml_att(node, "Color");
if (offset && color)
{
stops[count].offset = fz_atof(offset);
xps_parse_color(ctx, base_uri, color, &colorspace, sample);
fz_convert_color(colorspace, sample + 1, fz_device_rgb, rgb);
stops[count].r = rgb[0];
stops[count].g = rgb[1];
stops[count].b = rgb[2];
stops[count].a = sample[0];
count ++;
}
}
node = xml_next(node);
}
if (count == 0)
{
fz_warn("gradient brush has no gradient stops");
stops[0].offset = 0;
stops[0].r = 0;
stops[0].g = 0;
stops[0].b = 0;
stops[0].a = 1;
stops[1].offset = 1;
stops[1].r = 1;
stops[1].g = 1;
stops[1].b = 1;
stops[1].a = 1;
return 2;
}
if (count == maxcount)
fz_warn("gradient brush exceeded maximum number of gradient stops");
/* Postprocess to make sure the range of offsets is 0.0 to 1.0 */
qsort(stops, count, sizeof(struct stop), cmp_stop);
before = -1;
after = -1;
for (i = 0; i < count; i++)
{
if (stops[i].offset < 0)
before = i;
if (stops[i].offset > 1)
{
after = i;
break;
}
}
/* Remove all stops < 0 except the largest one */
if (before > 0)
{
memmove(stops, stops + before, (count - before) * sizeof(struct stop));
count -= before;
}
/* Remove all stops > 1 except the smallest one */
if (after >= 0)
count = after + 1;
/* Expand single stop to 0 .. 1 */
if (count == 1)
{
stops[1] = stops[0];
stops[0].offset = 0;
stops[1].offset = 1;
return 2;
}
/* First stop < 0 -- interpolate value to 0 */
if (stops[0].offset < 0)
{
float d = -stops[0].offset / (stops[1].offset - stops[0].offset);
stops[0].offset = 0;
stops[0].r = lerp(stops[0].r, stops[1].r, d);
stops[0].g = lerp(stops[0].g, stops[1].g, d);
stops[0].b = lerp(stops[0].b, stops[1].b, d);
stops[0].a = lerp(stops[0].a, stops[1].a, d);
}
/* Last stop > 1 -- interpolate value to 1 */
if (stops[count-1].offset > 1)
{
float d = (1 - stops[count-2].offset) / (stops[count-1].offset - stops[count-2].offset);
stops[count-1].offset = 1;
stops[0].r = lerp(stops[count-2].r, stops[count-1].r, d);
stops[0].g = lerp(stops[count-2].g, stops[count-1].g, d);
stops[0].b = lerp(stops[count-2].b, stops[count-1].b, d);
stops[0].a = lerp(stops[count-2].a, stops[count-1].a, d);
}
/* First stop > 0 -- insert a duplicate at 0 */
if (stops[0].offset > 0)
{
memmove(stops + 1, stops, count * sizeof(struct stop));
stops[0] = stops[1];
stops[0].offset = 0;
count++;
}
/* Last stop < 1 -- insert a duplicate at 1 */
if (stops[count-1].offset < 1)
{
stops[count] = stops[count-1];
stops[count].offset = 1;
count++;
}
return count;
}
static void
xps_sample_gradient_stops(fz_shade *shade, struct stop *stops, int count)
{
float offset, d;
int i, k;
k = 0;
for (i = 0; i < 256; i++)
{
offset = i / 255.0f;
while (k + 1 < count && offset > stops[k+1].offset)
k++;
d = (offset - stops[k].offset) / (stops[k+1].offset - stops[k].offset);
shade->function[i][0] = lerp(stops[k].r, stops[k+1].r, d);
shade->function[i][1] = lerp(stops[k].g, stops[k+1].g, d);
shade->function[i][2] = lerp(stops[k].b, stops[k+1].b, d);
shade->function[i][3] = lerp(stops[k].a, stops[k+1].a, d);
}
}
/*
* Radial gradients map more or less to Radial shadings.
* The inner circle is always a point.
* The outer circle is actually an ellipse,
* mess with the transform to squash the circle into the right aspect.
*/
static void
xps_draw_one_radial_gradient(xps_context *ctx, fz_matrix ctm,
struct stop *stops, int count,
int extend,
float x0, float y0, float r0,
float x1, float y1, float r1)
{
fz_shade *shade;
/* TODO: this (and the stuff in pdf_shade) should move to res_shade.c */
shade = fz_malloc(sizeof(fz_shade));
shade->refs = 1;
shade->colorspace = fz_device_rgb;
shade->bbox = fz_infinite_rect;
shade->matrix = fz_identity;
shade->use_background = 0;
shade->use_function = 1;
shade->type = FZ_RADIAL;
shade->extend[0] = extend;
shade->extend[1] = extend;
xps_sample_gradient_stops(shade, stops, count);
shade->mesh_len = 6;
shade->mesh_cap = 6;
shade->mesh = fz_calloc(shade->mesh_cap, sizeof(float));
shade->mesh[0] = x0;
shade->mesh[1] = y0;
shade->mesh[2] = r0;
shade->mesh[3] = x1;
shade->mesh[4] = y1;
shade->mesh[5] = r1;
fz_fill_shade(ctx->dev, shade, ctm, 1);
fz_drop_shade(shade);
}
/*
* Linear gradients map to Axial shadings.
*/
static void
xps_draw_one_linear_gradient(xps_context *ctx, fz_matrix ctm,
struct stop *stops, int count,
int extend,
float x0, float y0, float x1, float y1)
{
fz_shade *shade;
/* TODO: this (and the stuff in pdf_shade) should move to res_shade.c */
shade = fz_malloc(sizeof(fz_shade));
shade->refs = 1;
shade->colorspace = fz_device_rgb;
shade->bbox = fz_infinite_rect;
shade->matrix = fz_identity;
shade->use_background = 0;
shade->use_function = 1;
shade->type = FZ_LINEAR;
shade->extend[0] = extend;
shade->extend[1] = extend;
xps_sample_gradient_stops(shade, stops, count);
shade->mesh_len = 6;
shade->mesh_cap = 6;
shade->mesh = fz_calloc(shade->mesh_cap, sizeof(float));
shade->mesh[0] = x0;
shade->mesh[1] = y0;
shade->mesh[2] = 0;
shade->mesh[3] = x1;
shade->mesh[4] = y1;
shade->mesh[5] = 0;
fz_fill_shade(ctx->dev, shade, ctm, 1);
fz_drop_shade(shade);
}
/*
* We need to loop and create many shading objects to account
* for the Repeat and Reflect SpreadMethods.
* I'm not smart enough to calculate this analytically
* so we iterate and check each object until we
* reach a reasonable limit for infinite cases.
*/
static inline float point_inside_circle(float px, float py, float x, float y, float r)
{
float dx = px - x;
float dy = py - y;
return dx * dx + dy * dy <= r * r;
}
static void
xps_draw_radial_gradient(xps_context *ctx, fz_matrix ctm,
struct stop *stops, int count,
xml_element *root, int spread)
{
float x0, y0, r0;
float x1, y1, r1;
float xrad = 1;
float yrad = 1;
float invscale;
char *center_att = xml_att(root, "Center");
char *origin_att = xml_att(root, "GradientOrigin");
char *radius_x_att = xml_att(root, "RadiusX");
char *radius_y_att = xml_att(root, "RadiusY");
if (origin_att)
sscanf(origin_att, "%g,%g", &x0, &y0);
if (center_att)
sscanf(center_att, "%g,%g", &x1, &y1);
if (radius_x_att)
xrad = fz_atof(radius_x_att);
if (radius_y_att)
yrad = fz_atof(radius_y_att);
/* scale the ctm to make ellipses */
ctm = fz_concat(fz_scale(1, yrad / xrad), ctm);
invscale = xrad / yrad;
y0 = y0 * invscale;
y1 = y1 * invscale;
r0 = 0;
r1 = xrad;
xps_draw_one_radial_gradient(ctx, ctm, stops, count, 1, x0, y0, r0, x1, y1, r1);
}
/*
* Calculate how many iterations are needed to cover
* the bounding box.
*/
static void
xps_draw_linear_gradient(xps_context *ctx, fz_matrix ctm,
struct stop *stops, int count,
xml_element *root, int spread)
{
float x0, y0, x1, y1;
char *start_point_att = xml_att(root, "StartPoint");
char *end_point_att = xml_att(root, "EndPoint");
x0 = y0 = 0;
x1 = y1 = 1;
if (start_point_att)
sscanf(start_point_att, "%g,%g", &x0, &y0);
if (end_point_att)
sscanf(end_point_att, "%g,%g", &x1, &y1);
xps_draw_one_linear_gradient(ctx, ctm, stops, count, 1, x0, y0, x1, y1);
}
/*
* Parse XML tag and attributes for a gradient brush, create color/opacity
* function objects and call gradient drawing primitives.
*/
static void
xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area,
char *base_uri, xps_resource *dict, xml_element *root,
void (*draw)(xps_context *, fz_matrix, struct stop *, int, xml_element *, int))
{
xml_element *node;
char *opacity_att;
char *interpolation_att;
char *spread_att;
char *mapping_att;
char *transform_att;
xml_element *transform_tag = NULL;
xml_element *stop_tag = NULL;
struct stop stop_list[MAX_STOPS];
int stop_count;
fz_matrix transform;
int spread_method;
opacity_att = xml_att(root, "Opacity");
interpolation_att = xml_att(root, "ColorInterpolationMode");
spread_att = xml_att(root, "SpreadMethod");
mapping_att = xml_att(root, "MappingMode");
transform_att = xml_att(root, "Transform");
for (node = xml_down(root); node; node = xml_next(node))
{
if (!strcmp(xml_tag(node), "LinearGradientBrush.Transform"))
transform_tag = xml_down(node);
if (!strcmp(xml_tag(node), "RadialGradientBrush.Transform"))
transform_tag = xml_down(node);
if (!strcmp(xml_tag(node), "LinearGradientBrush.GradientStops"))
stop_tag = xml_down(node);
if (!strcmp(xml_tag(node), "RadialGradientBrush.GradientStops"))
stop_tag = xml_down(node);
}
xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL);
spread_method = SPREAD_PAD;
if (spread_att)
{
if (!strcmp(spread_att, "Pad"))
spread_method = SPREAD_PAD;
if (!strcmp(spread_att, "Reflect"))
spread_method = SPREAD_REFLECT;
if (!strcmp(spread_att, "Repeat"))
spread_method = SPREAD_REPEAT;
}
transform = fz_identity;
if (transform_att)
xps_parse_render_transform(ctx, transform_att, &transform);
if (transform_tag)
xps_parse_matrix_transform(ctx, transform_tag, &transform);
ctm = fz_concat(transform, ctm);
if (!stop_tag) {
fz_warn("missing gradient stops tag");
return;
}
stop_count = xps_parse_gradient_stops(ctx, base_uri, stop_tag, stop_list, MAX_STOPS);
if (stop_count == 0)
{
fz_warn("no gradient stops found");
return;
}
xps_begin_opacity(ctx, ctm, area, base_uri, dict, opacity_att, NULL);
draw(ctx, ctm, stop_list, stop_count, root, spread_method);
xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL);
}
void
xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area,
char *base_uri, xps_resource *dict, xml_element *root)
{
xps_parse_gradient_brush(ctx, ctm, area, base_uri, dict, root, xps_draw_linear_gradient);
}
void
xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area,
char *base_uri, xps_resource *dict, xml_element *root)
{
xps_parse_gradient_brush(ctx, ctm, area, base_uri, dict, root, xps_draw_radial_gradient);
}

View File

@ -1,128 +0,0 @@
#include "fitz.h"
#include "muxps.h"
static int
xps_decode_image(fz_pixmap **imagep, byte *buf, int len)
{
int error;
if (len < 8)
return fz_throw("unknown image file format");
if (buf[0] == 0xff && buf[1] == 0xd8)
{
error = xps_decode_jpeg(imagep, buf, len);
if (error)
return fz_rethrow(error, "cannot decode jpeg image");
}
else if (memcmp(buf, "\211PNG\r\n\032\n", 8) == 0)
{
error = xps_decode_png(imagep, buf, len);
if (error)
return fz_rethrow(error, "cannot decode png image");
}
else if (memcmp(buf, "II", 2) == 0 && buf[2] == 0xBC)
{
return fz_throw("JPEG-XR codec is not available");
}
else if (memcmp(buf, "MM", 2) == 0 || memcmp(buf, "II", 2) == 0)
{
error = xps_decode_tiff(imagep, buf, len);
if (error)
return fz_rethrow(error, "cannot decode TIFF image");
}
else
return fz_throw("unknown image file format");
return fz_okay;
}
static void
xps_paint_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict,
xml_element *root, void *vimage)
{
fz_pixmap *pixmap = vimage;
float xs = pixmap->w * 96 / pixmap->xres;
float ys = pixmap->h * 96 / pixmap->yres;
fz_matrix im = fz_scale(xs, -ys);
im.f = ys;
ctm = fz_concat(im, ctm);
fz_fill_image(ctx->dev, pixmap, ctm, ctx->opacity[ctx->opacity_top]);
}
static xps_part *
xps_find_image_brush_source_part(xps_context *ctx, char *base_uri, xml_element *root)
{
char *image_source_att;
char buf[1024];
char partname[1024];
char *image_name;
char *profile_name;
char *p;
image_source_att = xml_att(root, "ImageSource");
if (!image_source_att)
return NULL;
/* "{ColorConvertedBitmap /Resources/Image.tiff /Resources/Profile.icc}" */
if (strstr(image_source_att, "{ColorConvertedBitmap") == image_source_att)
{
image_name = NULL;
profile_name = NULL;
fz_strlcpy(buf, image_source_att, sizeof buf);
p = strchr(buf, ' ');
if (p)
{
image_name = p + 1;
p = strchr(p + 1, ' ');
if (p)
{
*p = 0;
profile_name = p + 1;
p = strchr(p + 1, '}');
if (p)
*p = 0;
}
}
}
else
{
image_name = image_source_att;
profile_name = NULL;
}
if (!image_name)
return NULL;
xps_absolute_path(partname, base_uri, image_name, sizeof partname);
return xps_read_part(ctx, partname);
}
void
xps_parse_image_brush(xps_context *ctx, fz_matrix ctm, fz_rect area,
char *base_uri, xps_resource *dict, xml_element *root)
{
xps_part *part;
fz_pixmap *image;
int code;
part = xps_find_image_brush_source_part(ctx, base_uri, root);
if (!part) {
fz_warn("cannot find image source");
return;
}
code = xps_decode_image(&image, part->data, part->size);
if (code < 0) {
xps_free_part(ctx, part);
fz_catch(-1, "cannot decode image resource");
return;
}
xps_parse_tiling_brush(ctx, ctm, area, base_uri, dict, root, xps_paint_image_brush, image);
fz_drop_pixmap(image);
xps_free_part(ctx, part);
}

View File

@ -1,138 +0,0 @@
#include "fitz.h"
#include "muxps.h"
#include <jpeglib.h>
#include <setjmp.h>
struct jpeg_error_mgr_jmp
{
struct jpeg_error_mgr super;
jmp_buf env;
char msg[JMSG_LENGTH_MAX];
};
static void error_exit(j_common_ptr cinfo)
{
struct jpeg_error_mgr_jmp *err = (struct jpeg_error_mgr_jmp *)cinfo->err;
cinfo->err->format_message(cinfo, err->msg);
longjmp(err->env, 1);
}
static void init_source(j_decompress_ptr cinfo)
{
/* nothing to do */
}
static void term_source(j_decompress_ptr cinfo)
{
/* nothing to do */
}
static boolean fill_input_buffer(j_decompress_ptr cinfo)
{
static unsigned char eoi[2] = { 0xFF, JPEG_EOI };
struct jpeg_source_mgr *src = cinfo->src;
src->next_input_byte = eoi;
src->bytes_in_buffer = 2;
return 1;
}
static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
{
struct jpeg_source_mgr *src = cinfo->src;
if (num_bytes > 0)
{
src->next_input_byte += num_bytes;
src->bytes_in_buffer -= num_bytes;
}
}
int
xps_decode_jpeg(fz_pixmap **imagep, byte *rbuf, int rlen)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr_jmp err;
struct jpeg_source_mgr src;
unsigned char *row[1], *sp, *dp;
fz_colorspace *colorspace;
unsigned int x;
int k;
fz_pixmap *image = NULL;
if (setjmp(err.env))
{
if (image)
fz_drop_pixmap(image);
return fz_throw("jpeg error: %s", err.msg);
}
cinfo.err = jpeg_std_error(&err.super);
err.super.error_exit = error_exit;
jpeg_create_decompress(&cinfo);
cinfo.src = &src;
src.init_source = init_source;
src.fill_input_buffer = fill_input_buffer;
src.skip_input_data = skip_input_data;
src.resync_to_restart = jpeg_resync_to_restart;
src.term_source = term_source;
src.next_input_byte = rbuf;
src.bytes_in_buffer = rlen;
jpeg_read_header(&cinfo, 1);
jpeg_start_decompress(&cinfo);
if (cinfo.output_components == 1)
colorspace = fz_device_gray;
else if (cinfo.output_components == 3)
colorspace = fz_device_rgb;
else if (cinfo.output_components == 4)
colorspace = fz_device_cmyk;
else
return fz_throw("bad number of components in jpeg: %d", cinfo.output_components);
image = fz_new_pixmap_with_limit(colorspace, cinfo.output_width, cinfo.output_height);
if (!image)
{
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return fz_throw("out of memory");
}
if (cinfo.density_unit == 1)
{
image->xres = cinfo.X_density;
image->yres = cinfo.Y_density;
}
else if (cinfo.density_unit == 2)
{
image->xres = cinfo.X_density * 254 / 100;
image->yres = cinfo.Y_density * 254 / 100;
}
fz_clear_pixmap(image);
row[0] = fz_malloc(cinfo.output_components * cinfo.output_width);
dp = image->samples;
while (cinfo.output_scanline < cinfo.output_height)
{
jpeg_read_scanlines(&cinfo, row, 1);
sp = row[0];
for (x = 0; x < cinfo.output_width; x++)
{
for (k = 0; k < cinfo.output_components; k++)
*dp++ = *sp++;
*dp++ = 255;
}
}
fz_free(row[0]);
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
*imagep = image;
return fz_okay;
}

View File

@ -1,990 +0,0 @@
#include "fitz.h"
#include "muxps.h"
static fz_point
fz_currentpoint(fz_path *path)
{
fz_point c, m;
int i;
c.x = c.y = m.x = m.y = 0;
i = 0;
while (i < path->len)
{
switch (path->items[i++].k)
{
case FZ_MOVETO:
m.x = c.x = path->items[i++].v;
m.y = c.y = path->items[i++].v;
break;
case FZ_LINETO:
c.x = path->items[i++].v;
c.y = path->items[i++].v;
break;
case FZ_CURVETO:
i += 4;
c.x = path->items[i++].v;
c.y = path->items[i++].v;
break;
case FZ_CLOSE_PATH:
c = m;
}
}
return c;
}
/* Draw an arc segment transformed by the matrix, we approximate with straight
* line segments. We cannot use the fz_arc function because they only draw
* circular arcs, we need to transform the line to make them elliptical but
* without transforming the line width.
*/
static void
xps_draw_arc_segment(fz_path *path, fz_matrix mtx, float th0, float th1, int iscw)
{
float t, d;
fz_point p;
while (th1 < th0)
th1 += (float)M_PI * 2;
d = (float)M_PI / 180; /* 1-degree precision */
if (iscw)
{
p.x = cosf(th0);
p.y = sinf(th0);
p = fz_transform_point(mtx, p);
fz_lineto(path, p.x, p.y);
for (t = th0; t < th1; t += d)
{
p.x = cosf(t);
p.y = sinf(t);
p = fz_transform_point(mtx, p);
fz_lineto(path, p.x, p.y);
}
p.x = cosf(th1);
p.y = sinf(th1);
p = fz_transform_point(mtx, p);
fz_lineto(path, p.x, p.y);
}
else
{
th0 += (float)M_PI * 2;
p.x = cosf(th0);
p.y = sinf(th0);
p = fz_transform_point(mtx, p);
fz_lineto(path, p.x, p.y);
for (t = th0; t > th1; t -= d)
{
p.x = cosf(t);
p.y = sinf(t);
p = fz_transform_point(mtx, p);
fz_lineto(path, p.x, p.y);
}
p.x = cosf(th1);
p.y = sinf(th1);
p = fz_transform_point(mtx, p);
fz_lineto(path, p.x, p.y);
}
}
/* Given two vectors find the angle between them. */
static float
angle_between(const fz_point u, const fz_point v)
{
float det = u.x * v.y - u.y * v.x;
float sign = (det < 0 ? -1 : 1);
float magu = u.x * u.x + u.y * u.y;
float magv = v.x * v.x + v.y * v.y;
float udotv = u.x * v.x + u.y * v.y;
float t = udotv / (magu * magv);
/* guard against rounding errors when near |1| (where acos will return NaN) */
if (t < -1) t = -1;
if (t > 1) t = 1;
return sign * acosf(t);
}
static void
xps_draw_arc(fz_path *path,
float size_x, float size_y, float rotation_angle,
int is_large_arc, int is_clockwise,
float point_x, float point_y)
{
fz_matrix rotmat, revmat;
fz_matrix mtx;
fz_point pt;
float rx, ry;
float x1, y1, x2, y2;
float x1t, y1t;
float cxt, cyt, cx, cy;
float t1, t2, t3;
float sign;
float th1, dth;
pt = fz_currentpoint(path);
x1 = pt.x;
y1 = pt.y;
x2 = point_x;
y2 = point_y;
rx = size_x;
ry = size_y;
if (is_clockwise != is_large_arc)
sign = 1;
else
sign = -1;
rotmat = fz_rotate(rotation_angle);
revmat = fz_rotate(-rotation_angle);
/* http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes */
/* Conversion from endpoint to center parameterization */
/* F.6.6.1 -- ensure radii are positive and non-zero */
rx = fabsf(rx);
ry = fabsf(ry);
if (rx < 0.001f || ry < 0.001f)
{
fz_lineto(path, x2, y2);
return;
}
/* F.6.5.1 */
pt.x = (x1 - x2) / 2;
pt.y = (y1 - y2) / 2;
pt = fz_transform_vector(revmat, pt);
x1t = pt.x;
y1t = pt.y;
/* F.6.6.2 -- ensure radii are large enough */
t1 = (x1t * x1t) / (rx * rx) + (y1t * y1t) / (ry * ry);
if (t1 > 1)
{
rx = rx * sqrtf(t1);
ry = ry * sqrtf(t1);
}
/* F.6.5.2 */
t1 = (rx * rx * ry * ry) - (rx * rx * y1t * y1t) - (ry * ry * x1t * x1t);
t2 = (rx * rx * y1t * y1t) + (ry * ry * x1t * x1t);
t3 = t1 / t2;
/* guard against rounding errors; sqrt of negative numbers is bad for your health */
if (t3 < 0) t3 = 0;
t3 = sqrtf(t3);
cxt = sign * t3 * (rx * y1t) / ry;
cyt = sign * t3 * -(ry * x1t) / rx;
/* F.6.5.3 */
pt.x = cxt;
pt.y = cyt;
pt = fz_transform_vector(rotmat, pt);
cx = pt.x + (x1 + x2) / 2;
cy = pt.y + (y1 + y2) / 2;
/* F.6.5.4 */
{
fz_point coord1, coord2, coord3, coord4;
coord1.x = 1;
coord1.y = 0;
coord2.x = (x1t - cxt) / rx;
coord2.y = (y1t - cyt) / ry;
coord3.x = (x1t - cxt) / rx;
coord3.y = (y1t - cyt) / ry;
coord4.x = (-x1t - cxt) / rx;
coord4.y = (-y1t - cyt) / ry;
th1 = angle_between(coord1, coord2);
dth = angle_between(coord3, coord4);
if (dth < 0 && !is_clockwise)
dth += (((float)M_PI / 180) * 360);
if (dth > 0 && is_clockwise)
dth -= (((float)M_PI / 180) * 360);
}
mtx = fz_identity;
mtx = fz_concat(fz_translate(cx, cy), mtx);
mtx = fz_concat(fz_rotate(rotation_angle), mtx);
mtx = fz_concat(fz_scale(rx, ry), mtx);
xps_draw_arc_segment(path, mtx, th1, th1 + dth, is_clockwise);
fz_lineto(path, point_x, point_y);
}
/*
* Parse an abbreviated geometry string, and call
* ghostscript moveto/lineto/curveto functions to
* build up a path.
*/
static fz_path *
xps_parse_abbreviated_geometry(xps_context *ctx, char *geom, int *fill_rule)
{
fz_path *path;
char **args;
char **pargs;
char *s = geom;
fz_point pt;
int i, n;
int cmd, old;
float x1, y1, x2, y2, x3, y3;
float smooth_x, smooth_y; /* saved cubic bezier control point for smooth curves */
int reset_smooth;
path = fz_new_path();
args = fz_calloc(strlen(geom) + 1, sizeof(char*));
pargs = args;
while (*s)
{
if ((*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z'))
{
*pargs++ = s++;
}
else if ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E')
{
*pargs++ = s;
while ((*s >= '0' && *s <= '9') || *s == '.' || *s == '+' || *s == '-' || *s == 'e' || *s == 'E')
s ++;
}
else
{
s++;
}
}
pargs[0] = s;
pargs[1] = 0;
n = pargs - args;
i = 0;
old = 0;
reset_smooth = 1;
smooth_x = 0;
smooth_y = 0;
while (i < n)
{
cmd = args[i][0];
if (cmd == '+' || cmd == '.' || cmd == '-' || (cmd >= '0' && cmd <= '9'))
cmd = old; /* it's a number, repeat old command */
else
i ++;
if (reset_smooth)
{
smooth_x = 0;
smooth_y = 0;
}
reset_smooth = 1;
switch (cmd)
{
case 'F':
*fill_rule = atoi(args[i]);
i ++;
break;
case 'M':
fz_moveto(path, fz_atof(args[i]), fz_atof(args[i+1]));
i += 2;
break;
case 'm':
pt = fz_currentpoint(path);
fz_moveto(path, pt.x + fz_atof(args[i]), pt.y + fz_atof(args[i+1]));
i += 2;
break;
case 'L':
fz_lineto(path, fz_atof(args[i]), fz_atof(args[i+1]));
i += 2;
break;
case 'l':
pt = fz_currentpoint(path);
fz_lineto(path, pt.x + fz_atof(args[i]), pt.y + fz_atof(args[i+1]));
i += 2;
break;
case 'H':
pt = fz_currentpoint(path);
fz_lineto(path, fz_atof(args[i]), pt.y);
i += 1;
break;
case 'h':
pt = fz_currentpoint(path);
fz_lineto(path, pt.x + fz_atof(args[i]), pt.y);
i += 1;
break;
case 'V':
pt = fz_currentpoint(path);
fz_lineto(path, pt.x, fz_atof(args[i]));
i += 1;
break;
case 'v':
pt = fz_currentpoint(path);
fz_lineto(path, pt.x, pt.y + fz_atof(args[i]));
i += 1;
break;
case 'C':
x1 = fz_atof(args[i+0]);
y1 = fz_atof(args[i+1]);
x2 = fz_atof(args[i+2]);
y2 = fz_atof(args[i+3]);
x3 = fz_atof(args[i+4]);
y3 = fz_atof(args[i+5]);
fz_curveto(path, x1, y1, x2, y2, x3, y3);
i += 6;
reset_smooth = 0;
smooth_x = x3 - x2;
smooth_y = y3 - y2;
break;
case 'c':
pt = fz_currentpoint(path);
x1 = fz_atof(args[i+0]) + pt.x;
y1 = fz_atof(args[i+1]) + pt.y;
x2 = fz_atof(args[i+2]) + pt.x;
y2 = fz_atof(args[i+3]) + pt.y;
x3 = fz_atof(args[i+4]) + pt.x;
y3 = fz_atof(args[i+5]) + pt.y;
fz_curveto(path, x1, y1, x2, y2, x3, y3);
i += 6;
reset_smooth = 0;
smooth_x = x3 - x2;
smooth_y = y3 - y2;
break;
case 'S':
pt = fz_currentpoint(path);
x1 = fz_atof(args[i+0]);
y1 = fz_atof(args[i+1]);
x2 = fz_atof(args[i+2]);
y2 = fz_atof(args[i+3]);
fz_curveto(path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2);
i += 4;
reset_smooth = 0;
smooth_x = x2 - x1;
smooth_y = y2 - y1;
break;
case 's':
pt = fz_currentpoint(path);
x1 = fz_atof(args[i+0]) + pt.x;
y1 = fz_atof(args[i+1]) + pt.y;
x2 = fz_atof(args[i+2]) + pt.x;
y2 = fz_atof(args[i+3]) + pt.y;
fz_curveto(path, pt.x + smooth_x, pt.y + smooth_y, x1, y1, x2, y2);
i += 4;
reset_smooth = 0;
smooth_x = x2 - x1;
smooth_y = y2 - y1;
break;
case 'Q':
pt = fz_currentpoint(path);
x1 = fz_atof(args[i+0]);
y1 = fz_atof(args[i+1]);
x2 = fz_atof(args[i+2]);
y2 = fz_atof(args[i+3]);
fz_curveto(path,
(pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3,
(x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3,
x2, y2);
i += 4;
break;
case 'q':
pt = fz_currentpoint(path);
x1 = fz_atof(args[i+0]) + pt.x;
y1 = fz_atof(args[i+1]) + pt.y;
x2 = fz_atof(args[i+2]) + pt.x;
y2 = fz_atof(args[i+3]) + pt.y;
fz_curveto(path,
(pt.x + 2 * x1) / 3, (pt.y + 2 * y1) / 3,
(x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3,
x2, y2);
i += 4;
break;
case 'A':
xps_draw_arc(path,
fz_atof(args[i+0]), fz_atof(args[i+1]), fz_atof(args[i+2]),
atoi(args[i+3]), atoi(args[i+4]),
fz_atof(args[i+5]), fz_atof(args[i+6]));
i += 7;
break;
case 'a':
pt = fz_currentpoint(path);
xps_draw_arc(path,
fz_atof(args[i+0]), fz_atof(args[i+1]), fz_atof(args[i+2]),
atoi(args[i+3]), atoi(args[i+4]),
fz_atof(args[i+5]) + pt.x, fz_atof(args[i+6]) + pt.y);
i += 7;
break;
case 'Z':
case 'z':
fz_closepath(path);
break;
default:
/* eek */
break;
}
old = cmd;
}
fz_free(args);
return path;
}
static void
xps_parse_arc_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke)
{
/* ArcSegment pretty much follows the SVG algorithm for converting an
* arc in endpoint representation to an arc in centerpoint
* representation. Once in centerpoint it can be given to the
* graphics library in the form of a postscript arc. */
float rotation_angle;
int is_large_arc, is_clockwise;
float point_x, point_y;
float size_x, size_y;
int is_stroked;
char *point_att = xml_att(root, "Point");
char *size_att = xml_att(root, "Size");
char *rotation_angle_att = xml_att(root, "RotationAngle");
char *is_large_arc_att = xml_att(root, "IsLargeArc");
char *sweep_direction_att = xml_att(root, "SweepDirection");
char *is_stroked_att = xml_att(root, "IsStroked");
if (!point_att || !size_att || !rotation_angle_att || !is_large_arc_att || !sweep_direction_att)
{
fz_warn("ArcSegment element is missing attributes");
return;
}
is_stroked = 1;
if (is_stroked_att && !strcmp(is_stroked_att, "false"))
is_stroked = 0;
if (!is_stroked)
*skipped_stroke = 1;
sscanf(point_att, "%g,%g", &point_x, &point_y);
sscanf(size_att, "%g,%g", &size_x, &size_y);
rotation_angle = fz_atof(rotation_angle_att);
is_large_arc = !strcmp(is_large_arc_att, "true");
is_clockwise = !strcmp(sweep_direction_att, "Clockwise");
if (stroking && !is_stroked)
{
fz_moveto(path, point_x, point_y);
return;
}
xps_draw_arc(path, size_x, size_y, rotation_angle, is_large_arc, is_clockwise, point_x, point_y);
}
static void
xps_parse_poly_quadratic_bezier_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke)
{
char *points_att = xml_att(root, "Points");
char *is_stroked_att = xml_att(root, "IsStroked");
float x[2], y[2];
int is_stroked;
fz_point pt;
char *s;
int n;
if (!points_att)
{
fz_warn("PolyQuadraticBezierSegment element has no points");
return;
}
is_stroked = 1;
if (is_stroked_att && !strcmp(is_stroked_att, "false"))
is_stroked = 0;
if (!is_stroked)
*skipped_stroke = 1;
s = points_att;
n = 0;
while (*s != 0)
{
while (*s == ' ') s++;
sscanf(s, "%g,%g", &x[n], &y[n]);
while (*s != ' ' && *s != 0) s++;
n ++;
if (n == 2)
{
if (stroking && !is_stroked)
{
fz_moveto(path, x[1], y[1]);
}
else
{
pt = fz_currentpoint(path);
fz_curveto(path,
(pt.x + 2 * x[0]) / 3, (pt.y + 2 * y[0]) / 3,
(x[1] + 2 * x[0]) / 3, (y[1] + 2 * y[0]) / 3,
x[1], y[1]);
}
n = 0;
}
}
}
static void
xps_parse_poly_bezier_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke)
{
char *points_att = xml_att(root, "Points");
char *is_stroked_att = xml_att(root, "IsStroked");
float x[3], y[3];
int is_stroked;
char *s;
int n;
if (!points_att)
{
fz_warn("PolyBezierSegment element has no points");
return;
}
is_stroked = 1;
if (is_stroked_att && !strcmp(is_stroked_att, "false"))
is_stroked = 0;
if (!is_stroked)
*skipped_stroke = 1;
s = points_att;
n = 0;
while (*s != 0)
{
while (*s == ' ') s++;
sscanf(s, "%g,%g", &x[n], &y[n]);
while (*s != ' ' && *s != 0) s++;
n ++;
if (n == 3)
{
if (stroking && !is_stroked)
fz_moveto(path, x[2], y[2]);
else
fz_curveto(path, x[0], y[0], x[1], y[1], x[2], y[2]);
n = 0;
}
}
}
static void
xps_parse_poly_line_segment(fz_path *path, xml_element *root, int stroking, int *skipped_stroke)
{
char *points_att = xml_att(root, "Points");
char *is_stroked_att = xml_att(root, "IsStroked");
int is_stroked;
float x, y;
char *s;
if (!points_att)
{
fz_warn("PolyLineSegment element has no points");
return;
}
is_stroked = 1;
if (is_stroked_att && !strcmp(is_stroked_att, "false"))
is_stroked = 0;
if (!is_stroked)
*skipped_stroke = 1;
s = points_att;
while (*s != 0)
{
while (*s == ' ') s++;
sscanf(s, "%g,%g", &x, &y);
if (stroking && !is_stroked)
fz_moveto(path, x, y);
else
fz_lineto(path, x, y);
while (*s != ' ' && *s != 0) s++;
}
}
static void
xps_parse_path_figure(fz_path *path, xml_element *root, int stroking)
{
xml_element *node;
char *is_closed_att;
char *start_point_att;
char *is_filled_att;
int is_closed = 0;
int is_filled = 1;
float start_x = 0;
float start_y = 0;
int skipped_stroke = 0;
is_closed_att = xml_att(root, "IsClosed");
start_point_att = xml_att(root, "StartPoint");
is_filled_att = xml_att(root, "IsFilled");
if (is_closed_att)
is_closed = !strcmp(is_closed_att, "true");
if (is_filled_att)
is_filled = !strcmp(is_filled_att, "true");
if (start_point_att)
sscanf(start_point_att, "%g,%g", &start_x, &start_y);
if (!stroking && !is_filled) /* not filled, when filling */
return;
fz_moveto(path, start_x, start_y);
for (node = xml_down(root); node; node = xml_next(node))
{
if (!strcmp(xml_tag(node), "ArcSegment"))
xps_parse_arc_segment(path, node, stroking, &skipped_stroke);
if (!strcmp(xml_tag(node), "PolyBezierSegment"))
xps_parse_poly_bezier_segment(path, node, stroking, &skipped_stroke);
if (!strcmp(xml_tag(node), "PolyLineSegment"))
xps_parse_poly_line_segment(path, node, stroking, &skipped_stroke);
if (!strcmp(xml_tag(node), "PolyQuadraticBezierSegment"))
xps_parse_poly_quadratic_bezier_segment(path, node, stroking, &skipped_stroke);
}
if (is_closed)
{
if (stroking && skipped_stroke)
fz_lineto(path, start_x, start_y); /* we've skipped using fz_moveto... */
else
fz_closepath(path); /* no skipped segments, safe to closepath properly */
}
}
fz_path *
xps_parse_path_geometry(xps_context *ctx, xps_resource *dict, xml_element *root, int stroking, int *fill_rule)
{
xml_element *node;
char *figures_att;
char *fill_rule_att;
char *transform_att;
xml_element *transform_tag = NULL;
xml_element *figures_tag = NULL; /* only used by resource */
fz_matrix transform;
fz_path *path;
figures_att = xml_att(root, "Figures");
fill_rule_att = xml_att(root, "FillRule");
transform_att = xml_att(root, "Transform");
for (node = xml_down(root); node; node = xml_next(node))
{
if (!strcmp(xml_tag(node), "PathGeometry.Transform"))
transform_tag = xml_down(node);
}
xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL);
xps_resolve_resource_reference(ctx, dict, &figures_att, &figures_tag, NULL);
if (fill_rule_att)
{
if (!strcmp(fill_rule_att, "NonZero"))
*fill_rule = 1;
if (!strcmp(fill_rule_att, "EvenOdd"))
*fill_rule = 0;
}
transform = fz_identity;
if (transform_att)
xps_parse_render_transform(ctx, transform_att, &transform);
if (transform_tag)
xps_parse_matrix_transform(ctx, transform_tag, &transform);
if (figures_att)
path = xps_parse_abbreviated_geometry(ctx, figures_att, fill_rule);
else
path = fz_new_path();
if (figures_tag)
xps_parse_path_figure(path, figures_tag, stroking);
for (node = xml_down(root); node; node = xml_next(node))
{
if (!strcmp(xml_tag(node), "PathFigure"))
xps_parse_path_figure(path, node, stroking);
}
if (transform_att || transform_tag)
fz_transform_path(path, transform);
return path;
}
static int
xps_parse_line_cap(char *attr)
{
if (attr)
{
if (!strcmp(attr, "Flat")) return 0;
if (!strcmp(attr, "Round")) return 1;
if (!strcmp(attr, "Square")) return 2;
if (!strcmp(attr, "Triangle")) return 3;
}
return 0;
}
void
xps_clip(xps_context *ctx, fz_matrix ctm, xps_resource *dict, char *clip_att, xml_element *clip_tag)
{
fz_path *path;
int fill_rule = 0;
if (clip_att)
path = xps_parse_abbreviated_geometry(ctx, clip_att, &fill_rule);
else if (clip_tag)
path = xps_parse_path_geometry(ctx, dict, clip_tag, 0, &fill_rule);
else
path = fz_new_path();
fz_clip_path(ctx->dev, path, NULL, fill_rule == 0, ctm);
fz_free_path(path);
}
/*
* Parse an XPS <Path> element, and call relevant ghostscript
* functions for drawing and/or clipping the child elements.
*/
void
xps_parse_path(xps_context *ctx, fz_matrix ctm, char *base_uri, xps_resource *dict, xml_element *root)
{
xml_element *node;
char *fill_uri;
char *stroke_uri;
char *opacity_mask_uri;
char *transform_att;
char *clip_att;
char *data_att;
char *fill_att;
char *stroke_att;
char *opacity_att;
char *opacity_mask_att;
xml_element *transform_tag = NULL;
xml_element *clip_tag = NULL;
xml_element *data_tag = NULL;
xml_element *fill_tag = NULL;
xml_element *stroke_tag = NULL;
xml_element *opacity_mask_tag = NULL;
char *fill_opacity_att = NULL;
char *stroke_opacity_att = NULL;
char *stroke_dash_array_att;
char *stroke_dash_cap_att;
char *stroke_dash_offset_att;
char *stroke_end_line_cap_att;
char *stroke_start_line_cap_att;
char *stroke_line_join_att;
char *stroke_miter_limit_att;
char *stroke_thickness_att;
fz_stroke_state stroke;
fz_matrix transform;
float samples[32];
fz_colorspace *colorspace;
fz_path *path;
fz_rect area;
int fill_rule;
/*
* Extract attributes and extended attributes.
*/
transform_att = xml_att(root, "RenderTransform");
clip_att = xml_att(root, "Clip");
data_att = xml_att(root, "Data");
fill_att = xml_att(root, "Fill");
stroke_att = xml_att(root, "Stroke");
opacity_att = xml_att(root, "Opacity");
opacity_mask_att = xml_att(root, "OpacityMask");
stroke_dash_array_att = xml_att(root, "StrokeDashArray");
stroke_dash_cap_att = xml_att(root, "StrokeDashCap");
stroke_dash_offset_att = xml_att(root, "StrokeDashOffset");
stroke_end_line_cap_att = xml_att(root, "StrokeEndLineCap");
stroke_start_line_cap_att = xml_att(root, "StrokeStartLineCap");
stroke_line_join_att = xml_att(root, "StrokeLineJoin");
stroke_miter_limit_att = xml_att(root, "StrokeMiterLimit");
stroke_thickness_att = xml_att(root, "StrokeThickness");
for (node = xml_down(root); node; node = xml_next(node))
{
if (!strcmp(xml_tag(node), "Path.RenderTransform"))
transform_tag = xml_down(node);
if (!strcmp(xml_tag(node), "Path.OpacityMask"))
opacity_mask_tag = xml_down(node);
if (!strcmp(xml_tag(node), "Path.Clip"))
clip_tag = xml_down(node);
if (!strcmp(xml_tag(node), "Path.Fill"))
fill_tag = xml_down(node);
if (!strcmp(xml_tag(node), "Path.Stroke"))
stroke_tag = xml_down(node);
if (!strcmp(xml_tag(node), "Path.Data"))
data_tag = xml_down(node);
}
fill_uri = base_uri;
stroke_uri = base_uri;
opacity_mask_uri = base_uri;
xps_resolve_resource_reference(ctx, dict, &data_att, &data_tag, NULL);
xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL);
xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL);
xps_resolve_resource_reference(ctx, dict, &fill_att, &fill_tag, &fill_uri);
xps_resolve_resource_reference(ctx, dict, &stroke_att, &stroke_tag, &stroke_uri);
xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);
/*
* Act on the information we have gathered:
*/
if (!data_att && !data_tag)
return;
if (fill_tag && !strcmp(xml_tag(fill_tag), "SolidColorBrush"))
{
fill_opacity_att = xml_att(fill_tag, "Opacity");
fill_att = xml_att(fill_tag, "Color");
fill_tag = NULL;
}
if (stroke_tag && !strcmp(xml_tag(stroke_tag), "SolidColorBrush"))
{
stroke_opacity_att = xml_att(stroke_tag, "Opacity");
stroke_att = xml_att(stroke_tag, "Color");
stroke_tag = NULL;
}
stroke.start_cap = xps_parse_line_cap(stroke_start_line_cap_att);
stroke.dash_cap = xps_parse_line_cap(stroke_dash_cap_att);
stroke.end_cap = xps_parse_line_cap(stroke_end_line_cap_att);
stroke.linejoin = 0;
if (stroke_line_join_att)
{
if (!strcmp(stroke_line_join_att, "Miter")) stroke.linejoin = 0;
if (!strcmp(stroke_line_join_att, "Round")) stroke.linejoin = 1;
if (!strcmp(stroke_line_join_att, "Bevel")) stroke.linejoin = 2;
}
stroke.miterlimit = 10;
if (stroke_miter_limit_att)
stroke.miterlimit = fz_atof(stroke_miter_limit_att);
stroke.linewidth = 1;
if (stroke_thickness_att)
stroke.linewidth = fz_atof(stroke_thickness_att);
stroke.dash_phase = 0;
stroke.dash_len = 0;
if (stroke_dash_array_att)
{
char *s = stroke_dash_array_att;
if (stroke_dash_offset_att)
stroke.dash_phase = fz_atof(stroke_dash_offset_att) * stroke.linewidth;
while (*s && stroke.dash_len < nelem(stroke.dash_list))
{
while (*s == ' ')
s++;
stroke.dash_list[stroke.dash_len++] = fz_atof(s) * stroke.linewidth;
while (*s && *s != ' ')
s++;
}
}
transform = fz_identity;
if (transform_att)
xps_parse_render_transform(ctx, transform_att, &transform);
if (transform_tag)
xps_parse_matrix_transform(ctx, transform_tag, &transform);
ctm = fz_concat(transform, ctm);
if (clip_att || clip_tag)
xps_clip(ctx, ctm, dict, clip_att, clip_tag);
fill_rule = 0;
if (data_att)
path = xps_parse_abbreviated_geometry(ctx, data_att, &fill_rule);
else if (data_tag)
path = xps_parse_path_geometry(ctx, dict, data_tag, 0, &fill_rule);
if (stroke_att || stroke_tag)
area = fz_bound_path(path, &stroke, ctm);
else
area = fz_bound_path(path, NULL, ctm);
xps_begin_opacity(ctx, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
if (fill_att)
{
xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples);
if (fill_opacity_att)
samples[0] = fz_atof(fill_opacity_att);
xps_set_color(ctx, colorspace, samples);
fz_fill_path(ctx->dev, path, fill_rule == 0, ctm,
ctx->colorspace, ctx->color, ctx->alpha);
}
if (fill_tag)
{
area = fz_bound_path(path, NULL, ctm);
fz_clip_path(ctx->dev, path, NULL, fill_rule == 0, ctm);
xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag);
fz_pop_clip(ctx->dev);
}
if (stroke_att)
{
xps_parse_color(ctx, base_uri, stroke_att, &colorspace, samples);
if (stroke_opacity_att)
samples[0] = fz_atof(stroke_opacity_att);
xps_set_color(ctx, colorspace, samples);
fz_stroke_path(ctx->dev, path, &stroke, ctm,
ctx->colorspace, ctx->color, ctx->alpha);
}
if (stroke_tag)
{
fz_clip_stroke_path(ctx->dev, path, NULL, &stroke, ctm);
xps_parse_brush(ctx, ctm, area, stroke_uri, dict, stroke_tag);
fz_pop_clip(ctx->dev);
}
xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
fz_free_path(path);
path = NULL;
if (clip_att || clip_tag)
fz_pop_clip(ctx->dev);
}

View File

@ -1,578 +0,0 @@
#include "fitz.h"
#include "muxps.h"
#include <zlib.h>
struct info
{
int width, height, depth, n;
int interlace, indexed;
int size;
unsigned char *samples;
unsigned char palette[256*4];
int transparency;
int trns[3];
int xres, yres;
};
static inline int getint(unsigned char *p)
{
return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
}
static inline int getcomp(unsigned char *line, int x, int bpc)
{
switch (bpc)
{
case 1: return (line[x >> 3] >> ( 7 - (x & 7) ) ) & 1;
case 2: return (line[x >> 2] >> ( ( 3 - (x & 3) ) << 1 ) ) & 3;
case 4: return (line[x >> 1] >> ( ( 1 - (x & 1) ) << 2 ) ) & 15;
case 8: return line[x];
case 16: return line[x << 1] << 8 | line[(x << 1) + 1];
}
return 0;
}
static inline void putcomp(unsigned char *line, int x, int bpc, int value)
{
int maxval = (1 << bpc) - 1;
switch (bpc)
{
case 1: line[x >> 3] &= ~(maxval << (7 - (x & 7))); break;
case 2: line[x >> 2] &= ~(maxval << ((3 - (x & 3)) << 1)); break;
case 4: line[x >> 1] &= ~(maxval << ((1 - (x & 1)) << 2)); break;
}
switch (bpc)
{
case 1: line[x >> 3] |= value << (7 - (x & 7)); break;
case 2: line[x >> 2] |= value << ((3 - (x & 3)) << 1); break;
case 4: line[x >> 1] |= value << ((1 - (x & 1)) << 2); break;
case 8: line[x] = value; break;
case 16: line[x << 1] = value >> 8; line[(x << 1) + 1] = value & 0xFF; break;
}
}
static const unsigned char png_signature[8] =
{
137, 80, 78, 71, 13, 10, 26, 10
};
static void *zalloc(void *opaque, unsigned int items, unsigned int size)
{
return fz_calloc(items, size);
}
static void zfree(void *opaque, void *address)
{
fz_free(address);
}
static inline int paeth(int a, int b, int c)
{
/* The definitions of ac and bc are correct, not a typo. */
int ac = b - c, bc = a - c, abcc = ac + bc;
int pa = (ac < 0 ? -ac : ac);
int pb = (bc < 0 ? -bc : bc);
int pc = (abcc < 0 ? -abcc : abcc);
return pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
}
static void
png_predict(unsigned char *samples, int width, int height, int n, int depth)
{
int stride = (width * n * depth + 7) / 8;
int bpp = (n * depth + 7) / 8;
int i, row;
for (row = 0; row < height; row ++)
{
unsigned char *src = samples + (stride + 1) * row;
unsigned char *dst = samples + stride * row;
unsigned char *a = dst;
unsigned char *b = dst - stride;
unsigned char *c = dst - stride;
switch (*src++)
{
default:
case 0: /* None */
for (i = 0; i < stride; i++)
*dst++ = *src++;
break;
case 1: /* Sub */
for (i = 0; i < bpp; i++)
*dst++ = *src++;
for (i = bpp; i < stride; i++)
*dst++ = *src++ + *a++;
break;
case 2: /* Up */
if (row == 0)
for (i = 0; i < stride; i++)
*dst++ = *src++;
else
for (i = 0; i < stride; i++)
*dst++ = *src++ + *b++;
break;
case 3: /* Average */
if (row == 0)
{
for (i = 0; i < bpp; i++)
*dst++ = *src++;
for (i = bpp; i < stride; i++)
*dst++ = *src++ + (*a++ >> 1);
}
else
{
for (i = 0; i < bpp; i++)
*dst++ = *src++ + (*b++ >> 1);
for (i = bpp; i < stride; i++)
*dst++ = *src++ + ((*b++ + *a++) >> 1);
}
break;
case 4: /* Paeth */
if (row == 0)
{
for (i = 0; i < bpp; i++)
*dst++ = *src++ + paeth(0, 0, 0);
for (i = bpp; i < stride; i++)
*dst++ = *src++ + paeth(*a++, 0, 0);
}
else
{
for (i = 0; i < bpp; i++)
*dst++ = *src++ + paeth(0, *b++, 0);
for (i = bpp; i < stride; i++)
*dst++ = *src++ + paeth(*a++, *b++, *c++);
}
break;
}
}
}
static const int adam7_ix[7] = { 0, 4, 0, 2, 0, 1, 0 };
static const int adam7_dx[7] = { 8, 8, 4, 4, 2, 2, 1 };
static const int adam7_iy[7] = { 0, 0, 4, 0, 2, 0, 1 };
static const int adam7_dy[7] = { 8, 8, 8, 4, 4, 2, 2 };
static void
png_deinterlace_passes(struct info *info, int *w, int *h, int *ofs)
{
int p, bpp = info->depth * info->n;
ofs[0] = 0;
for (p = 0; p < 7; p++)
{
w[p] = (info->width + adam7_dx[p] - adam7_ix[p] - 1) / adam7_dx[p];
h[p] = (info->height + adam7_dy[p] - adam7_iy[p] - 1) / adam7_dy[p];
if (w[p] == 0) h[p] = 0;
if (h[p] == 0) w[p] = 0;
if (w[p] && h[p])
ofs[p + 1] = ofs[p] + h[p] * (1 + (w[p] * bpp + 7) / 8);
else
ofs[p + 1] = ofs[p];
}
}
static void
png_deinterlace(struct info *info, int *passw, int *passh, int *passofs)
{
int n = info->n;
int depth = info->depth;
int stride = (info->width * n * depth + 7) / 8;
unsigned char *output;
int p, x, y, k;
output = fz_calloc(info->height, stride);
for (p = 0; p < 7; p++)
{
unsigned char *sp = info->samples + passofs[p];
int w = passw[p];
int h = passh[p];
png_predict(sp, w, h, n, depth);
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++)
{
int outx = x * adam7_dx[p] + adam7_ix[p];
int outy = y * adam7_dy[p] + adam7_iy[p];
unsigned char *dp = output + outy * stride;
for (k = 0; k < n; k++)
{
int v = getcomp(sp, x * n + k, depth);
putcomp(dp, outx * n + k, depth, v);
}
}
sp += (w * depth * n + 7) / 8;
}
}
fz_free(info->samples);
info->samples = output;
}
static int
png_read_ihdr(struct info *info, unsigned char *p, int size)
{
int color, compression, filter;
if (size != 13)
return fz_throw("IHDR chunk is the wrong size");
info->width = getint(p + 0);
info->height = getint(p + 4);
info->depth = p[8];
color = p[9];
compression = p[10];
filter = p[11];
info->interlace = p[12];
if (info->width <= 0)
return fz_throw("image width must be > 0");
if (info->height <= 0)
return fz_throw("image height must be > 0");
if (info->depth != 1 && info->depth != 2 && info->depth != 4 &&
info->depth != 8 && info->depth != 16)
return fz_throw("image bit depth must be one of 1, 2, 4, 8, 16");
if (color == 2 && info->depth < 8)
return fz_throw("illegal bit depth for truecolor");
if (color == 3 && info->depth > 8)
return fz_throw("illegal bit depth for indexed");
if (color == 4 && info->depth < 8)
return fz_throw("illegal bit depth for grayscale with alpha");
if (color == 6 && info->depth < 8)
return fz_throw("illegal bit depth for truecolor with alpha");
info->indexed = 0;
if (color == 0) /* gray */
info->n = 1;
else if (color == 2) /* rgb */
info->n = 3;
else if (color == 4) /* gray alpha */
info->n = 2;
else if (color == 6) /* rgb alpha */
info->n = 4;
else if (color == 3) /* indexed */
{
info->indexed = 1;
info->n = 1;
}
else
return fz_throw("unknown color type");
if (compression != 0)
return fz_throw("unknown compression method");
if (filter != 0)
return fz_throw("unknown filter method");
if (info->interlace != 0 && info->interlace != 1)
return fz_throw("interlace method not supported");
return fz_okay;
}
static int
png_read_plte(struct info *info, unsigned char *p, int size)
{
int n = size / 3;
int i;
if (n > 256 || n > (1 << info->depth))
return fz_throw("too many samples in palette");
for (i = 0; i < n; i++)
{
info->palette[i * 4] = p[i * 3];
info->palette[i * 4 + 1] = p[i * 3 + 1];
info->palette[i * 4 + 2] = p[i * 3 + 2];
}
return fz_okay;
}
static int
png_read_trns(struct info *info, unsigned char *p, int size)
{
int i;
info->transparency = 1;
if (info->indexed)
{
if (size > 256 || size > (1 << info->depth))
return fz_throw("too many samples in transparency table");
for (i = 0; i < size; i++)
info->palette[i * 4 + 3] = p[i];
}
else
{
if (size != info->n * 2)
return fz_throw("tRNS chunk is the wrong size");
for (i = 0; i < info->n; i++)
info->trns[i] = (p[i * 2] << 8 | p[i * 2 + 1]) & ((1 << info->depth) - 1);
}
return fz_okay;
}
static int
png_read_idat(struct info *info, unsigned char *p, int size, z_stream *stm)
{
int code;
stm->next_in = p;
stm->avail_in = size;
code = inflate(stm, Z_SYNC_FLUSH);
if (code != Z_OK && code != Z_STREAM_END)
return fz_throw("zlib error: %s", stm->msg);
if (stm->avail_in != 0)
{
if (stm->avail_out == 0)
return fz_throw("ran out of output before input");
return fz_throw("inflate did not consume buffer (%d remaining)", stm->avail_in);
}
return fz_okay;
}
static int
png_read_phys(struct info *info, unsigned char *p, int size)
{
if (size != 9)
return fz_throw("pHYs chunk is the wrong size");
if (p[8] == 1)
{
info->xres = getint(p) * 254 / 10000;
info->yres = getint(p + 4) * 254 / 10000;
}
return fz_okay;
}
static int
png_read_image(struct info *info, unsigned char *p, int total)
{
int passw[7], passh[7], passofs[8];
int code, size;
z_stream stm;
memset(info, 0, sizeof (struct info));
memset(info->palette, 255, sizeof(info->palette));
info->xres = 96;
info->yres = 96;
/* Read signature */
if (total < 8 + 12 || memcmp(p, png_signature, 8))
return fz_throw("not a png image (wrong signature)");
p += 8;
total -= 8;
/* Read IHDR chunk (must come first) */
size = getint(p);
if (size + 12 > total)
return fz_throw("premature end of data in png image");
if (!memcmp(p + 4, "IHDR", 4))
{
code = png_read_ihdr(info, p + 8, size);
if (code)
return fz_rethrow(code, "cannot read png header");
}
else
return fz_throw("png file must start with IHDR chunk");
p += size + 12;
total -= size + 12;
/* Prepare output buffer */
if (!info->interlace)
{
info->size = info->height * (1 + (info->width * info->n * info->depth + 7) / 8);
}
else
{
png_deinterlace_passes(info, passw, passh, passofs);
info->size = passofs[7];
}
info->samples = fz_malloc(info->size);
stm.zalloc = zalloc;
stm.zfree = zfree;
stm.opaque = NULL;
stm.next_out = info->samples;
stm.avail_out = info->size;
code = inflateInit(&stm);
if (code != Z_OK)
return fz_throw("zlib error: %s", stm.msg);
/* Read remaining chunks until IEND */
while (total > 8)
{
size = getint(p);
if (size + 12 > total)
return fz_throw("premature end of data in png image");
if (!memcmp(p + 4, "PLTE", 4))
{
code = png_read_plte(info, p + 8, size);
if (code)
return fz_rethrow(code, "cannot read png palette");
}
if (!memcmp(p + 4, "tRNS", 4))
{
code = png_read_trns(info, p + 8, size);
if (code)
return fz_rethrow(code, "cannot read png transparency");
}
if (!memcmp(p + 4, "pHYs", 4))
{
code = png_read_phys(info, p + 8, size);
if (code)
return fz_rethrow(code, "cannot read png resolution");
}
if (!memcmp(p + 4, "IDAT", 4))
{
code = png_read_idat(info, p + 8, size, &stm);
if (code)
return fz_rethrow(code, "cannot read png image data");
}
if (!memcmp(p + 4, "IEND", 4))
break;
p += size + 12;
total -= size + 12;
}
code = inflateEnd(&stm);
if (code != Z_OK)
return fz_throw("zlib error: %s", stm.msg);
/* Apply prediction filter and deinterlacing */
if (!info->interlace)
png_predict(info->samples, info->width, info->height, info->n, info->depth);
else
png_deinterlace(info, passw, passh, passofs);
return fz_okay;
}
static fz_pixmap *
png_expand_palette(struct info *info, fz_pixmap *src)
{
fz_pixmap *dst = fz_new_pixmap(fz_device_rgb, src->w, src->h);
unsigned char *sp = src->samples;
unsigned char *dp = dst->samples;
int x, y;
dst->xres = src->xres;
dst->yres = src->yres;
for (y = 0; y < info->height; y++)
{
for (x = 0; x < info->width; x++)
{
int v = *sp << 2;
*dp++ = info->palette[v];
*dp++ = info->palette[v + 1];
*dp++ = info->palette[v + 2];
*dp++ = info->palette[v + 3];
sp += 2;
}
}
fz_drop_pixmap(src);
return dst;
}
static void
png_mask_transparency(struct info *info, fz_pixmap *dst)
{
int stride = (info->width * info->n * info->depth + 7) / 8;
int depth = info->depth;
int n = info->n;
int x, y, k, t;
for (y = 0; y < info->height; y++)
{
unsigned char *sp = info->samples + y * stride;
unsigned char *dp = dst->samples + y * dst->w * dst->n;
for (x = 0; x < info->width; x++)
{
t = 1;
for (k = 0; k < n; k++)
if (getcomp(sp, x * n + k, depth) != info->trns[k])
t = 0;
if (t)
dp[x * dst->n + dst->n - 1] = 0;
}
}
}
int
xps_decode_png(fz_pixmap **imagep, byte *p, int total)
{
fz_pixmap *image;
fz_colorspace *colorspace;
struct info png;
int code;
int stride;
code = png_read_image(&png, p, total);
if (code)
return fz_rethrow(code, "cannot read png image");
if (png.n == 3 || png.n == 4)
colorspace = fz_device_rgb;
else
colorspace = fz_device_gray;
stride = (png.width * png.n * png.depth + 7) / 8;
image = fz_new_pixmap_with_limit(colorspace, png.width, png.height);
if (!image)
{
fz_free(png.samples);
return fz_throw("out of memory");
}
image->xres = png.xres;
image->yres = png.yres;
fz_unpack_tile(image, png.samples, png.n, png.depth, stride, png.indexed);
if (png.indexed)
image = png_expand_palette(&png, image);
else if (png.transparency)
png_mask_transparency(&png, image);
if (png.transparency || png.n == 2 || png.n == 4)
fz_premultiply_pixmap(image);
fz_free(png.samples);
*imagep = image;
return fz_okay;
}

View File

@ -1,187 +0,0 @@
#include "fitz.h"
#include "muxps.h"
static xml_element *
xps_find_resource(xps_context *ctx, xps_resource *dict, char *name, char **urip)
{
xps_resource *head, *node;
for (head = dict; head; head = head->parent)
{
for (node = head; node; node = node->next)
{
if (!strcmp(node->name, name))
{
if (urip && head->base_uri)
*urip = head->base_uri;
return node->data;
}
}
}
return NULL;
}
static xml_element *
xps_parse_resource_reference(xps_context *ctx, xps_resource *dict, char *att, char **urip)
{
char name[1024];
char *s;
if (strstr(att, "{StaticResource ") != att)
return NULL;
fz_strlcpy(name, att + 16, sizeof name);
s = strrchr(name, '}');
if (s)
*s = 0;
return xps_find_resource(ctx, dict, name, urip);
}
void
xps_resolve_resource_reference(xps_context *ctx, xps_resource *dict,
char **attp, xml_element **tagp, char **urip)
{
if (*attp)
{
xml_element *rsrc = xps_parse_resource_reference(ctx, dict, *attp, urip);
if (rsrc)
{
*attp = NULL;
*tagp = rsrc;
}
}
}
static int
xps_parse_remote_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base_uri, char *source_att)
{
char part_name[1024];
char part_uri[1024];
xps_resource *dict;
xps_part *part;
xml_element *xml;
char *s;
int code;
/* External resource dictionaries MUST NOT reference other resource dictionaries */
xps_absolute_path(part_name, base_uri, source_att, sizeof part_name);
part = xps_read_part(ctx, part_name);
if (!part)
{
return fz_throw("cannot find remote resource part '%s'", part_name);
}
xml = xml_parse_document(part->data, part->size);
if (!xml)
{
xps_free_part(ctx, part);
return fz_rethrow(-1, "cannot parse xml");
}
if (strcmp(xml_tag(xml), "ResourceDictionary"))
{
xml_free_element(xml);
xps_free_part(ctx, part);
return fz_throw("expected ResourceDictionary element (found %s)", xml_tag(xml));
}
fz_strlcpy(part_uri, part_name, sizeof part_uri);
s = strrchr(part_uri, '/');
if (s)
s[1] = 0;
code = xps_parse_resource_dictionary(ctx, &dict, part_uri, xml);
if (code)
{
xml_free_element(xml);
xps_free_part(ctx, part);
return fz_rethrow(code, "cannot parse remote resource dictionary: %s", part_uri);
}
dict->base_xml = xml; /* pass on ownership */
xps_free_part(ctx, part);
*dictp = dict;
return fz_okay;
}
int
xps_parse_resource_dictionary(xps_context *ctx, xps_resource **dictp, char *base_uri, xml_element *root)
{
xps_resource *head;
xps_resource *entry;
xml_element *node;
char *source;
char *key;
int code;
source = xml_att(root, "Source");
if (source)
{
code = xps_parse_remote_resource_dictionary(ctx, dictp, base_uri, source);
if (code)
return fz_rethrow(code, "cannot parse remote resource dictionary");
return fz_okay;
}
head = NULL;
for (node = xml_down(root); node; node = xml_next(node))
{
key = xml_att(node, "x:Key");
if (key)
{
entry = fz_malloc(sizeof(xps_resource));
entry->name = key;
entry->base_uri = NULL;
entry->base_xml = NULL;
entry->data = node;
entry->next = head;
entry->parent = NULL;
head = entry;
}
}
if (head)
head->base_uri = fz_strdup(base_uri);
else
return fz_throw("empty resource dictionary");
*dictp = head;
return fz_okay;
}
void
xps_free_resource_dictionary(xps_context *ctx, xps_resource *dict)
{
xps_resource *next;
while (dict)
{
next = dict->next;
if (dict->base_xml)
xml_free_element(dict->base_xml);
if (dict->base_uri)
fz_free(dict->base_uri);
fz_free(dict);
dict = next;
}
}
void
xps_debug_resource_dictionary(xps_resource *dict)
{
while (dict)
{
if (dict->base_uri)
printf("URI = '%s'\n", dict->base_uri);
printf("KEY = '%s' VAL = %p\n", dict->name, dict->data);
if (dict->parent)
{
printf("PARENT = {\n");
xps_debug_resource_dictionary(dict->parent);
printf("}\n");
}
dict = dict->next;
}
}

View File

@ -1,865 +0,0 @@
#include "fitz.h"
#include "muxps.h"
/*
* TIFF image loader. Should be enough to support TIFF files in XPS.
* Baseline TIFF 6.0 plus CMYK, LZW, Flate and JPEG support.
* Limited bit depths (1,2,4,8).
* Limited planar configurations (1=chunky).
* No tiles (easy fix if necessary).
* TODO: RGBPal images
*/
struct tiff
{
/* "file" */
byte *bp, *rp, *ep;
/* byte order */
unsigned order;
/* where we can find the strips of image data */
unsigned rowsperstrip;
unsigned *stripoffsets;
unsigned *stripbytecounts;
/* colormap */
unsigned *colormap;
/* assorted tags */
unsigned subfiletype;
unsigned photometric;
unsigned compression;
unsigned imagewidth;
unsigned imagelength;
unsigned samplesperpixel;
unsigned bitspersample;
unsigned planar;
unsigned extrasamples;
unsigned xresolution;
unsigned yresolution;
unsigned resolutionunit;
unsigned fillorder;
unsigned g3opts;
unsigned g4opts;
unsigned predictor;
unsigned ycbcrsubsamp[2];
byte *jpegtables; /* point into "file" buffer */
unsigned jpegtableslen;
byte *profile;
int profilesize;
/* decoded data */
fz_colorspace *colorspace;
byte *samples;
int stride;
};
enum
{
TII = 0x4949, /* 'II' */
TMM = 0x4d4d, /* 'MM' */
TBYTE = 1,
TASCII = 2,
TSHORT = 3,
TLONG = 4,
TRATIONAL = 5
};
#define NewSubfileType 254
#define ImageWidth 256
#define ImageLength 257
#define BitsPerSample 258
#define Compression 259
#define PhotometricInterpretation 262
#define FillOrder 266
#define StripOffsets 273
#define SamplesPerPixel 277
#define RowsPerStrip 278
#define StripByteCounts 279
#define XResolution 282
#define YResolution 283
#define PlanarConfiguration 284
#define T4Options 292
#define T6Options 293
#define ResolutionUnit 296
#define Predictor 317
#define ColorMap 320
#define TileWidth 322
#define TileLength 323
#define TileOffsets 324
#define TileByteCounts 325
#define ExtraSamples 338
#define JPEGTables 347
#define YCbCrSubSampling 520
#define ICCProfile 34675
static const byte bitrev[256] =
{
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};
static int
xps_decode_tiff_uncompressed(struct tiff *tiff, fz_stream *stm, byte *wp, int wlen)
{
int n = fz_read(stm, wp, wlen);
fz_close(stm);
if (n < 0)
return fz_rethrow(n, "cannot read uncompressed strip");
return fz_okay;
}
static int
xps_decode_tiff_packbits(struct tiff *tiff, fz_stream *chain, byte *wp, int wlen)
{
fz_stream *stm = fz_open_rld(chain);
int n = fz_read(stm, wp, wlen);
fz_close(stm);
if (n < 0)
return fz_rethrow(n, "cannot read packbits strip");
return fz_okay;
}
static int
xps_decode_tiff_lzw(struct tiff *tiff, fz_stream *chain, byte *wp, int wlen)
{
fz_stream *stm = fz_open_lzwd(chain, NULL);
int n = fz_read(stm, wp, wlen);
fz_close(stm);
if (n < 0)
return fz_rethrow(n, "cannot read lzw strip");
return fz_okay;
}
static int
xps_decode_tiff_flate(struct tiff *tiff, fz_stream *chain, byte *wp, int wlen)
{
fz_stream *stm = fz_open_flated(chain);
int n = fz_read(stm, wp, wlen);
fz_close(stm);
if (n < 0)
return fz_rethrow(n, "cannot read flate strip");
return fz_okay;
}
static int
xps_decode_tiff_fax(struct tiff *tiff, int comp, fz_stream *chain, byte *wp, int wlen)
{
fz_stream *stm;
fz_obj *params;
fz_obj *columns, *rows, *black_is_1, *k, *encoded_byte_align;
int n;
columns = fz_new_int(tiff->imagewidth);
rows = fz_new_int(tiff->imagelength);
black_is_1 = fz_new_bool(tiff->photometric == 0);
k = fz_new_int(comp == 4 ? -1 : 0);
encoded_byte_align = fz_new_bool(comp == 2);
params = fz_new_dict(5);
fz_dict_puts(params, "Columns", columns);
fz_dict_puts(params, "Rows", rows);
fz_dict_puts(params, "BlackIs1", black_is_1);
fz_dict_puts(params, "K", k);
fz_dict_puts(params, "EncodedByteAlign", encoded_byte_align);
fz_drop_obj(columns);
fz_drop_obj(rows);
fz_drop_obj(black_is_1);
fz_drop_obj(k);
fz_drop_obj(encoded_byte_align);
stm = fz_open_faxd(chain, params);
n = fz_read(stm, wp, wlen);
fz_close(stm);
fz_drop_obj(params);
if (n < 0)
return fz_rethrow(n, "cannot read fax strip");
return fz_okay;
}
static int
xps_decode_tiff_jpeg(struct tiff *tiff, fz_stream *chain, byte *wp, int wlen)
{
fz_stream *stm = fz_open_dctd(chain, NULL);
int n = fz_read(stm, wp, wlen);
fz_close(stm);
if (n < 0)
return fz_rethrow(n, "cannot read jpeg strip");
return fz_okay;
}
static inline int getcomp(byte *line, int x, int bpc)
{
switch (bpc)
{
case 1: return (line[x >> 3] >> ( 7 - (x & 7) ) ) & 1;
case 2: return (line[x >> 2] >> ( ( 3 - (x & 3) ) << 1 ) ) & 3;
case 4: return (line[x >> 1] >> ( ( 1 - (x & 1) ) << 2 ) ) & 15;
case 8: return line[x];
case 16: return line[x << 1] << 8 | line[(x << 1) + 1];
}
return 0;
}
static inline void putcomp(byte *line, int x, int bpc, int value)
{
int maxval = (1 << bpc) - 1;
switch (bpc)
{
case 1: line[x >> 3] &= ~(maxval << (7 - (x & 7))); break;
case 2: line[x >> 2] &= ~(maxval << ((3 - (x & 3)) << 1)); break;
case 4: line[x >> 1] &= ~(maxval << ((1 - (x & 1)) << 2)); break;
}
switch (bpc)
{
case 1: line[x >> 3] |= value << (7 - (x & 7)); break;
case 2: line[x >> 2] |= value << ((3 - (x & 3)) << 1); break;
case 4: line[x >> 1] |= value << ((1 - (x & 1)) << 2); break;
case 8: line[x] = value; break;
case 16: line[x << 1] = value >> 8; line[(x << 1) + 1] = value & 0xFF; break;
}
}
static void
xps_unpredict_tiff(byte *line, int width, int comps, int bits)
{
byte left[32];
int i, k, v;
for (k = 0; k < comps; k++)
left[k] = 0;
for (i = 0; i < width; i++)
{
for (k = 0; k < comps; k++)
{
v = getcomp(line, i * comps + k, bits);
v = v + left[k];
v = v % (1 << bits);
putcomp(line, i * comps + k, bits, v);
left[k] = v;
}
}
}
static void
xps_invert_tiff(byte *line, int width, int comps, int bits, int alpha)
{
int i, k, v;
int m = (1 << bits) - 1;
for (i = 0; i < width; i++)
{
for (k = 0; k < comps; k++)
{
v = getcomp(line, i * comps + k, bits);
if (!alpha || k < comps - 1)
v = m - v;
putcomp(line, i * comps + k, bits, v);
}
}
}
static int
xps_expand_tiff_colormap(struct tiff *tiff)
{
int maxval = 1 << tiff->bitspersample;
byte *samples;
byte *src, *dst;
unsigned int x, y;
unsigned int stride;
/* colormap has first all red, then all green, then all blue values */
/* colormap values are 0..65535, bits is 4 or 8 */
/* image can be with or without extrasamples: comps is 1 or 2 */
if (tiff->samplesperpixel != 1 && tiff->samplesperpixel != 2)
return fz_throw("invalid number of samples for RGBPal");
if (tiff->bitspersample != 4 && tiff->bitspersample != 8)
return fz_throw("invalid number of bits for RGBPal");
stride = tiff->imagewidth * (tiff->samplesperpixel + 2);
samples = fz_malloc(stride * tiff->imagelength);
for (y = 0; y < tiff->imagelength; y++)
{
src = tiff->samples + (tiff->stride * y);
dst = samples + (stride * y);
for (x = 0; x < tiff->imagewidth; x++)
{
if (tiff->extrasamples)
{
int c = getcomp(src, x * 2, tiff->bitspersample);
int a = getcomp(src, x * 2 + 1, tiff->bitspersample);
*dst++ = tiff->colormap[c + 0] >> 8;
*dst++ = tiff->colormap[c + maxval] >> 8;
*dst++ = tiff->colormap[c + maxval * 2] >> 8;
*dst++ = a << (8 - tiff->bitspersample);
}
else
{
int c = getcomp(src, x, tiff->bitspersample);
*dst++ = tiff->colormap[c + 0] >> 8;
*dst++ = tiff->colormap[c + maxval] >> 8;
*dst++ = tiff->colormap[c + maxval * 2] >> 8;
}
}
}
tiff->samplesperpixel += 2;
tiff->bitspersample = 8;
tiff->stride = stride;
tiff->samples = samples;
return fz_okay;
}
static int
xps_decode_tiff_strips(struct tiff *tiff)
{
fz_stream *stm;
int error;
/* switch on compression to create a filter */
/* feed each strip to the filter */
/* read out the data and pack the samples into an xps_image */
/* type 32773 / packbits -- nothing special (same row-padding as PDF) */
/* type 2 / ccitt rle -- no EOL, no RTC, rows are byte-aligned */
/* type 3 and 4 / g3 and g4 -- each strip starts new section */
/* type 5 / lzw -- each strip is handled separately */
byte *wp;
unsigned row;
unsigned strip;
unsigned i;
if (!tiff->rowsperstrip || !tiff->stripoffsets || !tiff->rowsperstrip)
return fz_throw("no image data in tiff; maybe it is tiled");
if (tiff->planar != 1)
return fz_throw("image data is not in chunky format");
tiff->stride = (tiff->imagewidth * tiff->samplesperpixel * tiff->bitspersample + 7) / 8;
switch (tiff->photometric)
{
case 0: /* WhiteIsZero -- inverted */
tiff->colorspace = fz_device_gray;
break;
case 1: /* BlackIsZero */
tiff->colorspace = fz_device_gray;
break;
case 2: /* RGB */
tiff->colorspace = fz_device_rgb;
break;
case 3: /* RGBPal */
tiff->colorspace = fz_device_rgb;
break;
case 5: /* CMYK */
tiff->colorspace = fz_device_cmyk;
break;
case 6: /* YCbCr */
/* it's probably a jpeg ... we let jpeg convert to rgb */
tiff->colorspace = fz_device_rgb;
break;
default:
return fz_throw("unknown photometric: %d", tiff->photometric);
}
switch (tiff->resolutionunit)
{
case 2:
/* no unit conversion needed */
break;
case 3:
tiff->xresolution = tiff->xresolution * 254 / 100;
tiff->yresolution = tiff->yresolution * 254 / 100;
break;
default:
tiff->xresolution = 96;
tiff->yresolution = 96;
break;
}
/* Note xres and yres could be 0 even if unit was set. If so default to 96dpi. */
if (tiff->xresolution == 0 || tiff->yresolution == 0)
{
tiff->xresolution = 96;
tiff->yresolution = 96;
}
tiff->samples = fz_calloc(tiff->imagelength, tiff->stride);
memset(tiff->samples, 0x55, tiff->imagelength * tiff->stride);
wp = tiff->samples;
strip = 0;
for (row = 0; row < tiff->imagelength; row += tiff->rowsperstrip)
{
unsigned offset = tiff->stripoffsets[strip];
unsigned rlen = tiff->stripbytecounts[strip];
unsigned wlen = tiff->stride * tiff->rowsperstrip;
byte *rp = tiff->bp + offset;
if (wp + wlen > tiff->samples + tiff->stride * tiff->imagelength)
wlen = tiff->samples + tiff->stride * tiff->imagelength - wp;
if (rp + rlen > tiff->ep)
return fz_throw("strip extends beyond the end of the file");
/* the bits are in un-natural order */
if (tiff->fillorder == 2)
for (i = 0; i < rlen; i++)
rp[i] = bitrev[rp[i]];
/* the strip decoders will close this */
stm = fz_open_memory(rp, rlen);
switch (tiff->compression)
{
case 1:
error = xps_decode_tiff_uncompressed(tiff, stm, wp, wlen);
break;
case 2:
error = xps_decode_tiff_fax(tiff, 2, stm, wp, wlen);
break;
case 3:
error = xps_decode_tiff_fax(tiff, 3, stm, wp, wlen);
break;
case 4:
error = xps_decode_tiff_fax(tiff, 4, stm, wp, wlen);
break;
case 5:
error = xps_decode_tiff_lzw(tiff, stm, wp, wlen);
break;
case 6:
error = fz_throw("deprecated JPEG in TIFF compression not supported");
break;
case 7:
error = xps_decode_tiff_jpeg(tiff, stm, wp, wlen);
break;
case 8:
error = xps_decode_tiff_flate(tiff, stm, wp, wlen);
break;
case 32773:
error = xps_decode_tiff_packbits(tiff, stm, wp, wlen);
break;
default:
error = fz_throw("unknown TIFF compression: %d", tiff->compression);
}
if (error)
return fz_rethrow(error, "cannot decode strip %d", row / tiff->rowsperstrip);
/* scramble the bits back into original order */
if (tiff->fillorder == 2)
for (i = 0; i < rlen; i++)
rp[i] = bitrev[rp[i]];
wp += tiff->stride * tiff->rowsperstrip;
strip ++;
}
/* Predictor (only for LZW and Flate) */
if ((tiff->compression == 5 || tiff->compression == 8) && tiff->predictor == 2)
{
byte *p = tiff->samples;
for (i = 0; i < tiff->imagelength; i++)
{
xps_unpredict_tiff(p, tiff->imagewidth, tiff->samplesperpixel, tiff->bitspersample);
p += tiff->stride;
}
}
/* RGBPal */
if (tiff->photometric == 3 && tiff->colormap)
{
error = xps_expand_tiff_colormap(tiff);
if (error)
return fz_rethrow(error, "cannot expand colormap");
}
/* WhiteIsZero .. invert */
if (tiff->photometric == 0)
{
byte *p = tiff->samples;
for (i = 0; i < tiff->imagelength; i++)
{
xps_invert_tiff(p, tiff->imagewidth, tiff->samplesperpixel, tiff->bitspersample, tiff->extrasamples);
p += tiff->stride;
}
}
return fz_okay;
}
static inline int readbyte(struct tiff *tiff)
{
if (tiff->rp < tiff->ep)
return *tiff->rp++;
return EOF;
}
static inline unsigned readshort(struct tiff *tiff)
{
unsigned a = readbyte(tiff);
unsigned b = readbyte(tiff);
if (tiff->order == TII)
return (b << 8) | a;
return (a << 8) | b;
}
static inline unsigned readlong(struct tiff *tiff)
{
unsigned a = readbyte(tiff);
unsigned b = readbyte(tiff);
unsigned c = readbyte(tiff);
unsigned d = readbyte(tiff);
if (tiff->order == TII)
return (d << 24) | (c << 16) | (b << 8) | a;
return (a << 24) | (b << 16) | (c << 8) | d;
}
static void
xps_read_tiff_bytes(unsigned char *p, struct tiff *tiff, unsigned ofs, unsigned n)
{
tiff->rp = tiff->bp + ofs;
if (tiff->rp > tiff->ep)
tiff->rp = tiff->bp;
while (n--)
*p++ = readbyte(tiff);
}
static void
xps_read_tiff_tag_value(unsigned *p, struct tiff *tiff, unsigned type, unsigned ofs, unsigned n)
{
tiff->rp = tiff->bp + ofs;
if (tiff->rp > tiff->ep)
tiff->rp = tiff->bp;
while (n--)
{
switch (type)
{
case TRATIONAL:
*p = readlong(tiff);
*p = *p / readlong(tiff);
p ++;
break;
case TBYTE: *p++ = readbyte(tiff); break;
case TSHORT: *p++ = readshort(tiff); break;
case TLONG: *p++ = readlong(tiff); break;
default: *p++ = 0; break;
}
}
}
static int
xps_read_tiff_tag(struct tiff *tiff, unsigned offset)
{
unsigned tag;
unsigned type;
unsigned count;
unsigned value;
tiff->rp = tiff->bp + offset;
tag = readshort(tiff);
type = readshort(tiff);
count = readlong(tiff);
if ((type == TBYTE && count <= 4) ||
(type == TSHORT && count <= 2) ||
(type == TLONG && count <= 1))
value = tiff->rp - tiff->bp;
else
value = readlong(tiff);
switch (tag)
{
case NewSubfileType:
xps_read_tiff_tag_value(&tiff->subfiletype, tiff, type, value, 1);
break;
case ImageWidth:
xps_read_tiff_tag_value(&tiff->imagewidth, tiff, type, value, 1);
break;
case ImageLength:
xps_read_tiff_tag_value(&tiff->imagelength, tiff, type, value, 1);
break;
case BitsPerSample:
xps_read_tiff_tag_value(&tiff->bitspersample, tiff, type, value, 1);
break;
case Compression:
xps_read_tiff_tag_value(&tiff->compression, tiff, type, value, 1);
break;
case PhotometricInterpretation:
xps_read_tiff_tag_value(&tiff->photometric, tiff, type, value, 1);
break;
case FillOrder:
xps_read_tiff_tag_value(&tiff->fillorder, tiff, type, value, 1);
break;
case SamplesPerPixel:
xps_read_tiff_tag_value(&tiff->samplesperpixel, tiff, type, value, 1);
break;
case RowsPerStrip:
xps_read_tiff_tag_value(&tiff->rowsperstrip, tiff, type, value, 1);
break;
case XResolution:
xps_read_tiff_tag_value(&tiff->xresolution, tiff, type, value, 1);
break;
case YResolution:
xps_read_tiff_tag_value(&tiff->yresolution, tiff, type, value, 1);
break;
case PlanarConfiguration:
xps_read_tiff_tag_value(&tiff->planar, tiff, type, value, 1);
break;
case T4Options:
xps_read_tiff_tag_value(&tiff->g3opts, tiff, type, value, 1);
break;
case T6Options:
xps_read_tiff_tag_value(&tiff->g4opts, tiff, type, value, 1);
break;
case Predictor:
xps_read_tiff_tag_value(&tiff->predictor, tiff, type, value, 1);
break;
case ResolutionUnit:
xps_read_tiff_tag_value(&tiff->resolutionunit, tiff, type, value, 1);
break;
case YCbCrSubSampling:
xps_read_tiff_tag_value(tiff->ycbcrsubsamp, tiff, type, value, 2);
break;
case ExtraSamples:
xps_read_tiff_tag_value(&tiff->extrasamples, tiff, type, value, 1);
break;
case ICCProfile:
tiff->profile = fz_malloc(count);
/* ICC profile data type is set to UNDEFINED.
* TBYTE reading not correct in xps_read_tiff_tag_value */
xps_read_tiff_bytes(tiff->profile, tiff, value, count);
tiff->profilesize = count;
break;
case JPEGTables:
fz_warn("jpeg tables in tiff not implemented");
tiff->jpegtables = tiff->bp + value;
tiff->jpegtableslen = count;
break;
case StripOffsets:
tiff->stripoffsets = fz_calloc(count, sizeof(unsigned));
xps_read_tiff_tag_value(tiff->stripoffsets, tiff, type, value, count);
break;
case StripByteCounts:
tiff->stripbytecounts = fz_calloc(count, sizeof(unsigned));
xps_read_tiff_tag_value(tiff->stripbytecounts, tiff, type, value, count);
break;
case ColorMap:
tiff->colormap = fz_calloc(count, sizeof(unsigned));
xps_read_tiff_tag_value(tiff->colormap, tiff, type, value, count);
break;
case TileWidth:
case TileLength:
case TileOffsets:
case TileByteCounts:
return fz_throw("tiled tiffs not supported");
default:
/* printf("unknown tag: %d t=%d n=%d\n", tag, type, count); */
break;
}
return fz_okay;
}
static void
xps_swap_byte_order(byte *buf, int n)
{
int i, t;
for (i = 0; i < n; i++)
{
t = buf[i * 2 + 0];
buf[i * 2 + 0] = buf[i * 2 + 1];
buf[i * 2 + 1] = t;
}
}
static int
xps_decode_tiff_header(struct tiff *tiff, byte *buf, int len)
{
unsigned version;
unsigned offset;
unsigned count;
unsigned i;
int error;
memset(tiff, 0, sizeof(struct tiff));
tiff->bp = buf;
tiff->rp = buf;
tiff->ep = buf + len;
/* tag defaults, where applicable */
tiff->bitspersample = 1;
tiff->compression = 1;
tiff->samplesperpixel = 1;
tiff->resolutionunit = 2;
tiff->rowsperstrip = 0xFFFFFFFF;
tiff->fillorder = 1;
tiff->planar = 1;
tiff->subfiletype = 0;
tiff->predictor = 1;
tiff->ycbcrsubsamp[0] = 2;
tiff->ycbcrsubsamp[1] = 2;
/*
* Read IFH
*/
/* get byte order marker */
tiff->order = TII;
tiff->order = readshort(tiff);
if (tiff->order != TII && tiff->order != TMM)
return fz_throw("not a TIFF file, wrong magic marker");
/* check version */
version = readshort(tiff);
if (version != 42)
return fz_throw("not a TIFF file, wrong version marker");
/* get offset of IFD */
offset = readlong(tiff);
/*
* Read IFD
*/
tiff->rp = tiff->bp + offset;
count = readshort(tiff);
offset += 2;
for (i = 0; i < count; i++)
{
error = xps_read_tiff_tag(tiff, offset);
if (error)
return fz_rethrow(error, "cannot read TIFF header tag");
offset += 12;
}
return fz_okay;
}
int
xps_decode_tiff(fz_pixmap **imagep, byte *buf, int len)
{
int error;
fz_pixmap *image;
struct tiff tiff;
error = xps_decode_tiff_header(&tiff, buf, len);
if (error)
return fz_rethrow(error, "cannot decode tiff header");
/* Decode the image strips */
if (tiff.rowsperstrip > tiff.imagelength)
tiff.rowsperstrip = tiff.imagelength;
error = xps_decode_tiff_strips(&tiff);
if (error)
return fz_rethrow(error, "cannot decode image data");
/* Byte swap 16-bit images to big endian if necessary */
if (tiff.bitspersample == 16)
{
if (tiff.order == TII)
xps_swap_byte_order(tiff.samples, tiff.imagewidth * tiff.imagelength * tiff.samplesperpixel);
}
/* Expand into fz_pixmap struct */
image = fz_new_pixmap_with_limit(tiff.colorspace, tiff.imagewidth, tiff.imagelength);
if (!image)
{
if (tiff.colormap) fz_free(tiff.colormap);
if (tiff.stripoffsets) fz_free(tiff.stripoffsets);
if (tiff.stripbytecounts) fz_free(tiff.stripbytecounts);
if (tiff.samples) fz_free(tiff.samples);
return fz_throw("out of memory");
}
image->xres = tiff.xresolution;
image->yres = tiff.yresolution;
fz_unpack_tile(image, tiff.samples, tiff.samplesperpixel, tiff.bitspersample, tiff.stride, 0);
/* We should only do this on non-pre-multiplied images, but files in the wild are bad */
if (tiff.extrasamples /* == 2 */)
{
/* CMYK is a subtractive colorspace, we want additive for premul alpha */
if (image->n == 5)
{
fz_pixmap *rgb = fz_new_pixmap(fz_device_rgb, image->w, image->h);
fz_convert_pixmap(image, rgb);
rgb->xres = image->xres;
rgb->yres = image->yres;
fz_drop_pixmap(image);
image = rgb;
}
fz_premultiply_pixmap(image);
}
/* Clean up scratch memory */
if (tiff.colormap) fz_free(tiff.colormap);
if (tiff.stripoffsets) fz_free(tiff.stripoffsets);
if (tiff.stripbytecounts) fz_free(tiff.stripbytecounts);
if (tiff.samples) fz_free(tiff.samples);
*imagep = image;
return fz_okay;
}

View File

@ -1,365 +0,0 @@
#include "fitz.h"
#include "muxps.h"
#define TILE
/*
* Parse a tiling brush (visual and image brushes at this time) common
* properties. Use the callback to draw the individual tiles.
*/
enum { TILE_NONE, TILE_TILE, TILE_FLIP_X, TILE_FLIP_Y, TILE_FLIP_X_Y };
struct closure
{
char *base_uri;
xps_resource *dict;
xml_element *root;
void *user;
void (*func)(xps_context*, fz_matrix, fz_rect, char*, xps_resource*, xml_element*, void*);
};
static void
xps_paint_tiling_brush_clipped(xps_context *ctx, fz_matrix ctm, fz_rect viewbox, struct closure *c)
{
fz_path *path = fz_new_path();
fz_moveto(path, viewbox.x0, viewbox.y0);
fz_lineto(path, viewbox.x0, viewbox.y1);
fz_lineto(path, viewbox.x1, viewbox.y1);
fz_lineto(path, viewbox.x1, viewbox.y0);
fz_closepath(path);
fz_clip_path(ctx->dev, path, NULL, 0, ctm);
fz_free_path(path);
c->func(ctx, ctm, viewbox, c->base_uri, c->dict, c->root, c->user);
fz_pop_clip(ctx->dev);
}
static void
xps_paint_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect viewbox, int tile_mode, struct closure *c)
{
fz_matrix ttm;
xps_paint_tiling_brush_clipped(ctx, ctm, viewbox, c);
if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y)
{
ttm = fz_concat(fz_translate(viewbox.x1 * 2, 0), ctm);
ttm = fz_concat(fz_scale(-1, 1), ttm);
xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c);
}
if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y)
{
ttm = fz_concat(fz_translate(0, viewbox.y1 * 2), ctm);
ttm = fz_concat(fz_scale(1, -1), ttm);
xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c);
}
if (tile_mode == TILE_FLIP_X_Y)
{
ttm = fz_concat(fz_translate(viewbox.x1 * 2, viewbox.y1 * 2), ctm);
ttm = fz_concat(fz_scale(-1, -1), ttm);
xps_paint_tiling_brush_clipped(ctx, ttm, viewbox, c);
}
}
void
xps_parse_tiling_brush(xps_context *ctx, fz_matrix ctm, fz_rect area,
char *base_uri, xps_resource *dict, xml_element *root,
void (*func)(xps_context*, fz_matrix, fz_rect, char*, xps_resource*, xml_element*, void*), void *user)
{
xml_element *node;
struct closure c;
char *opacity_att;
char *transform_att;
char *viewbox_att;
char *viewport_att;
char *tile_mode_att;
char *viewbox_units_att;
char *viewport_units_att;
xml_element *transform_tag = NULL;
fz_matrix transform;
fz_rect viewbox;
fz_rect viewport;
float xstep, ystep;
float xscale, yscale;
int tile_mode;
opacity_att = xml_att(root, "Opacity");
transform_att = xml_att(root, "Transform");
viewbox_att = xml_att(root, "Viewbox");
viewport_att = xml_att(root, "Viewport");
tile_mode_att = xml_att(root, "TileMode");
viewbox_units_att = xml_att(root, "ViewboxUnits");
viewport_units_att = xml_att(root, "ViewportUnits");
c.base_uri = base_uri;
c.dict = dict;
c.root = root;
c.user = user;
c.func = func;
for (node = xml_down(root); node; node = xml_next(node))
{
if (!strcmp(xml_tag(node), "ImageBrush.Transform"))
transform_tag = xml_down(node);
if (!strcmp(xml_tag(node), "VisualBrush.Transform"))
transform_tag = xml_down(node);
}
xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL);
transform = fz_identity;
if (transform_att)
xps_parse_render_transform(ctx, transform_att, &transform);
if (transform_tag)
xps_parse_matrix_transform(ctx, transform_tag, &transform);
ctm = fz_concat(transform, ctm);
viewbox = fz_unit_rect;
if (viewbox_att)
xps_parse_rectangle(ctx, viewbox_att, &viewbox);
viewport = fz_unit_rect;
if (viewport_att)
xps_parse_rectangle(ctx, viewport_att, &viewport);
/* some sanity checks on the viewport/viewbox size */
if (fabsf(viewport.x1 - viewport.x0) < 0.01f) return;
if (fabsf(viewport.y1 - viewport.y0) < 0.01f) return;
if (fabsf(viewbox.x1 - viewbox.x0) < 0.01f) return;
if (fabsf(viewbox.y1 - viewbox.y0) < 0.01f) return;
xstep = viewbox.x1 - viewbox.x0;
ystep = viewbox.y1 - viewbox.y0;
xscale = (viewport.x1 - viewport.x0) / xstep;
yscale = (viewport.y1 - viewport.y0) / ystep;
tile_mode = TILE_NONE;
if (tile_mode_att)
{
if (!strcmp(tile_mode_att, "None"))
tile_mode = TILE_NONE;
if (!strcmp(tile_mode_att, "Tile"))
tile_mode = TILE_TILE;
if (!strcmp(tile_mode_att, "FlipX"))
tile_mode = TILE_FLIP_X;
if (!strcmp(tile_mode_att, "FlipY"))
tile_mode = TILE_FLIP_Y;
if (!strcmp(tile_mode_att, "FlipXY"))
tile_mode = TILE_FLIP_X_Y;
}
if (tile_mode == TILE_FLIP_X || tile_mode == TILE_FLIP_X_Y)
xstep *= 2;
if (tile_mode == TILE_FLIP_Y || tile_mode == TILE_FLIP_X_Y)
ystep *= 2;
xps_begin_opacity(ctx, ctm, area, base_uri, dict, opacity_att, NULL);
ctm = fz_concat(fz_translate(viewport.x0, viewport.y0), ctm);
ctm = fz_concat(fz_scale(xscale, yscale), ctm);
ctm = fz_concat(fz_translate(-viewbox.x0, -viewbox.y0), ctm);
if (tile_mode != TILE_NONE)
{
int x0, y0, x1, y1;
fz_matrix invctm = fz_invert_matrix(ctm);
area = fz_transform_rect(invctm, area);
x0 = floorf(area.x0 / xstep);
y0 = floorf(area.y0 / ystep);
x1 = ceilf(area.x1 / xstep);
y1 = ceilf(area.y1 / ystep);
#ifdef TILE
{
int n = (x1 - x0) * (y1 - y0);
fz_rect bigview = viewbox;
bigview.x1 = bigview.x0 + xstep;
bigview.y1 = bigview.y0 + ystep;
if (n > 1)
fz_begin_tile(ctx->dev, area, bigview, xstep, ystep, ctm);
if (n > 0)
xps_paint_tiling_brush(ctx, ctm, viewbox, tile_mode, &c);
if (n > 1)
fz_end_tile(ctx->dev);
}
#else
{
int x, y;
for (y = y0; y < y1; y++)
{
for (x = x0; x < x1; x++)
{
fz_matrix ttm = fz_concat(fz_translate(xstep * x, ystep * y), ctm);
xps_paint_tiling_brush(ctx, ttm, viewbox, tile_mode, &c);
}
}
}
#endif
}
else
{
xps_paint_tiling_brush(ctx, ctm, viewbox, tile_mode, &c);
}
xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL);
}
static void
xps_paint_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area,
char *base_uri, xps_resource *dict, xml_element *root, void *visual_tag)
{
xps_parse_element(ctx, ctm, area, base_uri, dict, (xml_element *)visual_tag);
}
void
xps_parse_visual_brush(xps_context *ctx, fz_matrix ctm, fz_rect area,
char *base_uri, xps_resource *dict, xml_element *root)
{
xml_element *node;
char *visual_uri;
char *visual_att;
xml_element *visual_tag = NULL;
visual_att = xml_att(root, "Visual");
for (node = xml_down(root); node; node = xml_next(node))
{
if (!strcmp(xml_tag(node), "VisualBrush.Visual"))
visual_tag = xml_down(node);
}
visual_uri = base_uri;
xps_resolve_resource_reference(ctx, dict, &visual_att, &visual_tag, &visual_uri);
if (visual_tag)
{
xps_parse_tiling_brush(ctx, ctm, area,
visual_uri, dict, root, xps_paint_visual_brush, visual_tag);
}
}
void
xps_parse_canvas(xps_context *ctx, fz_matrix ctm, fz_rect area, char *base_uri, xps_resource *dict, xml_element *root)
{
xps_resource *new_dict = NULL;
xml_element *node;
char *opacity_mask_uri;
int code;
char *transform_att;
char *clip_att;
char *opacity_att;
char *opacity_mask_att;
xml_element *transform_tag = NULL;
xml_element *clip_tag = NULL;
xml_element *opacity_mask_tag = NULL;
fz_matrix transform;
transform_att = xml_att(root, "RenderTransform");
clip_att = xml_att(root, "Clip");
opacity_att = xml_att(root, "Opacity");
opacity_mask_att = xml_att(root, "OpacityMask");
for (node = xml_down(root); node; node = xml_next(node))
{
if (!strcmp(xml_tag(node), "Canvas.Resources") && xml_down(node))
{
code = xps_parse_resource_dictionary(ctx, &new_dict, base_uri, xml_down(node));
if (code)
fz_catch(code, "cannot load Canvas.Resources");
else
{
new_dict->parent = dict;
dict = new_dict;
}
}
if (!strcmp(xml_tag(node), "Canvas.RenderTransform"))
transform_tag = xml_down(node);
if (!strcmp(xml_tag(node), "Canvas.Clip"))
clip_tag = xml_down(node);
if (!strcmp(xml_tag(node), "Canvas.OpacityMask"))
opacity_mask_tag = xml_down(node);
}
opacity_mask_uri = base_uri;
xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL);
xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL);
xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);
transform = fz_identity;
if (transform_att)
xps_parse_render_transform(ctx, transform_att, &transform);
if (transform_tag)
xps_parse_matrix_transform(ctx, transform_tag, &transform);
ctm = fz_concat(transform, ctm);
if (clip_att || clip_tag)
xps_clip(ctx, ctm, dict, clip_att, clip_tag);
xps_begin_opacity(ctx, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
for (node = xml_down(root); node; node = xml_next(node))
{
xps_parse_element(ctx, ctm, area, base_uri, dict, node);
}
xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
if (clip_att || clip_tag)
fz_pop_clip(ctx->dev);
if (new_dict)
xps_free_resource_dictionary(ctx, new_dict);
}
void
xps_parse_fixed_page(xps_context *ctx, fz_matrix ctm, xps_page *page)
{
xml_element *node;
xps_resource *dict;
char base_uri[1024];
fz_rect area;
char *s;
int code;
fz_strlcpy(base_uri, page->name, sizeof base_uri);
s = strrchr(base_uri, '/');
if (s)
s[1] = 0;
dict = NULL;
ctx->opacity_top = 0;
ctx->opacity[0] = 1;
if (!page->root)
return;
area = fz_transform_rect(fz_scale(page->width, page->height), fz_unit_rect);
for (node = xml_down(page->root); node; node = xml_next(node))
{
if (!strcmp(xml_tag(node), "FixedPage.Resources") && xml_down(node))
{
code = xps_parse_resource_dictionary(ctx, &dict, base_uri, xml_down(node));
if (code)
fz_catch(code, "cannot load FixedPage.Resources");
}
xps_parse_element(ctx, ctm, area, base_uri, dict, node);
}
if (dict)
{
xps_free_resource_dictionary(ctx, dict);
}
}

View File

@ -1,94 +0,0 @@
#include "fitz.h"
#include "muxps.h"
static inline int xps_tolower(int c)
{
if (c >= 'A' && c <= 'Z')
return c + 32;
return c;
}
int
xps_strcasecmp(char *a, char *b)
{
while (xps_tolower(*a) == xps_tolower(*b))
{
if (*a++ == 0)
return 0;
b++;
}
return xps_tolower(*a) - xps_tolower(*b);
}
#define SEP(x) ((x)=='/' || (x) == 0)
static char *
xps_clean_path(char *name)
{
char *p, *q, *dotdot;
int rooted;
rooted = name[0] == '/';
/*
* invariants:
* p points at beginning of path element we're considering.
* q points just past the last path element we wrote (no slash).
* dotdot points just past the point where .. cannot backtrack
* any further (no slash).
*/
p = q = dotdot = name + rooted;
while (*p)
{
if(p[0] == '/') /* null element */
p++;
else if (p[0] == '.' && SEP(p[1]))
p += 1; /* don't count the separator in case it is nul */
else if (p[0] == '.' && p[1] == '.' && SEP(p[2]))
{
p += 2;
if (q > dotdot) /* can backtrack */
{
while(--q > dotdot && *q != '/')
;
}
else if (!rooted) /* /.. is / but ./../ is .. */
{
if (q != name)
*q++ = '/';
*q++ = '.';
*q++ = '.';
dotdot = q;
}
}
else /* real path element */
{
if (q != name+rooted)
*q++ = '/';
while ((*q = *p) != '/' && *q != 0)
p++, q++;
}
}
if (q == name) /* empty string is really "." */
*q++ = '.';
*q = '\0';
return name;
}
void
xps_absolute_path(char *output, char *base_uri, char *path, int output_size)
{
if (path[0] == '/')
{
fz_strlcpy(output, path, output_size);
}
else
{
fz_strlcpy(output, base_uri, output_size);
fz_strlcat(output, "/", output_size);
fz_strlcat(output, path, output_size);
}
xps_clean_path(output);
}

View File

@ -1,387 +0,0 @@
#include "fitz.h"
#include "muxps.h"
struct attribute
{
char name[40];
char *value;
struct attribute *next;
};
struct element
{
char name[40];
struct attribute *atts;
struct element *up, *down, *next;
};
struct parser
{
struct element *head;
};
static inline void indent(int n)
{
while (n--) putchar(' ');
}
void xml_print_element(struct element *item, int level)
{
while (item) {
struct attribute *att;
indent(level);
printf("<%s", item->name);
for (att = item->atts; att; att = att->next)
printf(" %s=\"%s\"", att->name, att->value);
if (item->down) {
printf(">\n");
xml_print_element(item->down, level + 1);
indent(level);
printf("</%s>\n", item->name);
}
else {
printf("/>\n");
}
item = item->next;
}
}
struct element *xml_next(struct element *item)
{
return item->next;
}
struct element *xml_down(struct element *item)
{
return item->down;
}
char *xml_tag(struct element *item)
{
return item->name;
}
char *xml_att(struct element *item, const char *name)
{
struct attribute *att;
for (att = item->atts; att; att = att->next)
if (!strcmp(att->name, name))
return att->value;
return NULL;
}
static void xml_free_attribute(struct attribute *att)
{
while (att) {
struct attribute *next = att->next;
if (att->value)
fz_free(att->value);
fz_free(att);
att = next;
}
}
void xml_free_element(struct element *item)
{
while (item) {
struct element *next = item->next;
if (item->atts)
xml_free_attribute(item->atts);
if (item->down)
xml_free_element(item->down);
fz_free(item);
item = next;
}
}
static int xml_parse_entity(int *c, char *a)
{
char *b;
if (a[1] == '#') {
if (a[2] == 'x')
*c = strtol(a + 3, &b, 16);
else
*c = strtol(a + 2, &b, 10);
if (*b == ';')
return b - a + 1;
}
else if (a[1] == 'l' && a[2] == 't' && a[3] == ';') {
*c = '<';
return 4;
}
else if (a[1] == 'g' && a[2] == 't' && a[3] == ';') {
*c = '>';
return 4;
}
else if (a[1] == 'a' && a[2] == 'm' && a[3] == 'p' && a[4] == ';') {
*c = '&';
return 5;
}
else if (a[1] == 'a' && a[2] == 'p' && a[3] == 'o' && a[4] == 's' && a[5] == ';') {
*c = '\'';
return 6;
}
else if (a[1] == 'q' && a[2] == 'u' && a[3] == 'o' && a[4] == 't' && a[5] == ';') {
*c = '"';
return 6;
}
*c = *a++;
return 1;
}
static void xml_emit_open_tag(struct parser *parser, char *a, char *b)
{
struct element *head, *tail;
head = fz_malloc(sizeof(struct element));
if (b - a > sizeof(head->name))
b = a + sizeof(head->name);
memcpy(head->name, a, b - a);
head->name[b - a] = 0;
head->atts = NULL;
head->up = parser->head;
head->down = NULL;
head->next = NULL;
if (!parser->head->down) {
parser->head->down = head;
}
else {
tail = parser->head->down;
while (tail->next)
tail = tail->next;
tail->next = head;
}
parser->head = head;
}
static void xml_emit_att_name(struct parser *parser, char *a, char *b)
{
struct element *head = parser->head;
struct attribute *att;
att = fz_malloc(sizeof(struct attribute));
if (b - a > sizeof(att->name))
b = a + sizeof(att->name);
memcpy(att->name, a, b - a);
att->name[b - a] = 0;
att->value = NULL;
att->next = head->atts;
head->atts = att;
}
static void xml_emit_att_value(struct parser *parser, char *a, char *b)
{
struct element *head = parser->head;
struct attribute *att = head->atts;
char *s;
int c;
/* entities are all longer than UTFmax so runetochar is safe */
s = att->value = fz_malloc(b - a + 1);
while (a < b) {
if (*a == '&') {
a += xml_parse_entity(&c, a);
s += runetochar(s, &c);
}
else {
*s++ = *a++;
}
}
*s = 0;
}
static void xml_emit_close_tag(struct parser *parser)
{
if (parser->head->up)
parser->head = parser->head->up;
}
static inline int isname(int c)
{
return c == '.' || c == '-' || c == '_' || c == ':' ||
(c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z');
}
static inline int iswhite(int c)
{
return c == ' ' || c == '\r' || c == '\n' || c == '\t';
}
static char *xml_parse_document_imp(struct parser *x, char *p)
{
char *mark;
int quote;
parse_text:
mark = p;
while (*p && *p != '<') ++p;
if (*p == '<') { ++p; goto parse_element; }
return NULL;
parse_element:
if (*p == '/') { ++p; goto parse_closing_element; }
if (*p == '!') { ++p; goto parse_comment; }
if (*p == '?') { ++p; goto parse_processing_instruction; }
while (iswhite(*p)) ++p;
if (isname(*p))
goto parse_element_name;
return "syntax error in element";
parse_comment:
if (*p == '[') goto parse_cdata;
if (*p++ != '-') return "syntax error in comment (<! not followed by --)";
if (*p++ != '-') return "syntax error in comment (<!- not followed by -)";
mark = p;
while (*p) {
if (p[0] == '-' && p[1] == '-' && p[2] == '>') {
p += 3;
goto parse_text;
}
++p;
}
return "end of data in comment";
parse_cdata:
if (p[1] != 'C' || p[2] != 'D' || p[3] != 'A' || p[4] != 'T' || p[5] != 'A' || p[6] != '[')
return "syntax error in CDATA section";
p += 7;
mark = p;
while (*p) {
if (p[0] == ']' && p[1] == ']' && p[2] == '>') {
p += 3;
goto parse_text;
}
++p;
}
return "end of data in CDATA section";
parse_processing_instruction:
while (*p) {
if (p[0] == '?' && p[1] == '>') {
p += 2;
goto parse_text;
}
++p;
}
return "end of data in processing instruction";
parse_closing_element:
while (iswhite(*p)) ++p;
mark = p;
while (isname(*p)) ++p;
while (iswhite(*p)) ++p;
if (*p != '>')
return "syntax error in closing element";
xml_emit_close_tag(x);
++p;
goto parse_text;
parse_element_name:
mark = p;
while (isname(*p)) ++p;
xml_emit_open_tag(x, mark, p);
if (*p == '>') { ++p; goto parse_text; }
if (p[0] == '/' && p[1] == '>') {
xml_emit_close_tag(x);
p += 2;
goto parse_text;
}
if (iswhite(*p))
goto parse_attributes;
return "syntax error after element name";
parse_attributes:
while (iswhite(*p)) ++p;
if (isname(*p))
goto parse_attribute_name;
if (*p == '>') { ++p; goto parse_text; }
if (p[0] == '/' && p[1] == '>') {
xml_emit_close_tag(x);
p += 2;
goto parse_text;
}
return "syntax error in attributes";
parse_attribute_name:
mark = p;
while (isname(*p)) ++p;
xml_emit_att_name(x, mark, p);
while (iswhite(*p)) ++p;
if (*p == '=') { ++p; goto parse_attribute_value; }
return "syntax error after attribute name";
parse_attribute_value:
while (iswhite(*p)) ++p;
quote = *p++;
if (quote != '"' && quote != '\'')
return "missing quote character";
mark = p;
while (*p && *p != quote) ++p;
if (*p == quote) {
xml_emit_att_value(x, mark, p++);
goto parse_attributes;
}
return "end of data in attribute value";
}
static char *convert_to_utf8(unsigned char *s, int n)
{
unsigned char *e = s + n;
char *dst, *d;
int c;
if (s[0] == 0xFE && s[1] == 0xFF) {
dst = d = fz_malloc(n * 2);
while (s + 1 < e) {
c = s[0] << 8 | s[1];
d += runetochar(d, &c);
s += 2;
}
*d = 0;
return dst;
}
if (s[0] == 0xFF && s[1] == 0xFE) {
dst = d = fz_malloc(n * 2);
while (s + 1 < e) {
c = s[0] | s[1] << 8;
d += runetochar(d, &c);
s += 2;
}
*d = 0;
return dst;
}
return (char*)s;
}
struct element *
xml_parse_document(unsigned char *s, int n)
{
struct parser parser;
struct element root;
char *p, *error;
/* s is already null-terminated (see xps_new_part) */
memset(&root, 0, sizeof(root));
parser.head = &root;
p = convert_to_utf8(s, n);
error = xml_parse_document_imp(&parser, p);
if (error) {
fz_throw(error);
return NULL;
}
if (p != (char*)s)
fz_free(p);
return root.down;
}

View File

@ -1,501 +0,0 @@
#include "fitz.h"
#include "muxps.h"
#include <zlib.h>
xps_part *
xps_new_part(xps_context *ctx, char *name, int size)
{
xps_part *part;
part = fz_malloc(sizeof(xps_part));
part->name = fz_strdup(name);
part->size = size;
part->data = fz_malloc(size + 1);
part->data[size] = 0; /* null-terminate for xml parser */
return part;
}
void
xps_free_part(xps_context *ctx, xps_part *part)
{
fz_free(part->name);
fz_free(part->data);
fz_free(part);
}
static inline int getshort(fz_stream *file)
{
int a = fz_read_byte(file);
int b = fz_read_byte(file);
return a | b << 8;
}
static inline int getlong(fz_stream *file)
{
int a = fz_read_byte(file);
int b = fz_read_byte(file);
int c = fz_read_byte(file);
int d = fz_read_byte(file);
return a | b << 8 | c << 16 | d << 24;
}
static void *
xps_zip_alloc_items(xps_context *ctx, int items, int size)
{
return fz_calloc(items, size);
}
static void
xps_zip_free(xps_context *ctx, void *ptr)
{
fz_free(ptr);
}
static int
xps_compare_entries(const void *a0, const void *b0)
{
xps_entry *a = (xps_entry*) a0;
xps_entry *b = (xps_entry*) b0;
return xps_strcasecmp(a->name, b->name);
}
static xps_entry *
xps_find_zip_entry(xps_context *ctx, char *name)
{
int l = 0;
int r = ctx->zip_count - 1;
while (l <= r)
{
int m = (l + r) >> 1;
int c = xps_strcasecmp(name, ctx->zip_table[m].name);
if (c < 0)
r = m - 1;
else if (c > 0)
l = m + 1;
else
return &ctx->zip_table[m];
}
return NULL;
}
static int
xps_read_zip_entry(xps_context *ctx, xps_entry *ent, unsigned char *outbuf)
{
z_stream stream;
unsigned char *inbuf;
int sig;
int version, general, method;
int namelength, extralength;
int code;
fz_seek(ctx->file, ent->offset, 0);
sig = getlong(ctx->file);
if (sig != ZIP_LOCAL_FILE_SIG)
return fz_throw("wrong zip local file signature (0x%x)", sig);
version = getshort(ctx->file);
general = getshort(ctx->file);
method = getshort(ctx->file);
(void) getshort(ctx->file); /* file time */
(void) getshort(ctx->file); /* file date */
(void) getlong(ctx->file); /* crc-32 */
(void) getlong(ctx->file); /* csize */
(void) getlong(ctx->file); /* usize */
namelength = getshort(ctx->file);
extralength = getshort(ctx->file);
fz_seek(ctx->file, namelength + extralength, 1);
if (method == 0)
{
fz_read(ctx->file, outbuf, ent->usize);
}
else if (method == 8)
{
inbuf = fz_malloc(ent->csize);
fz_read(ctx->file, inbuf, ent->csize);
memset(&stream, 0, sizeof(z_stream));
stream.zalloc = (alloc_func) xps_zip_alloc_items;
stream.zfree = (free_func) xps_zip_free;
stream.opaque = ctx;
stream.next_in = inbuf;
stream.avail_in = ent->csize;
stream.next_out = outbuf;
stream.avail_out = ent->usize;
code = inflateInit2(&stream, -15);
if (code != Z_OK)
return fz_throw("zlib inflateInit2 error: %s", stream.msg);
code = inflate(&stream, Z_FINISH);
if (code != Z_STREAM_END)
{
inflateEnd(&stream);
return fz_throw("zlib inflate error: %s", stream.msg);
}
code = inflateEnd(&stream);
if (code != Z_OK)
return fz_throw("zlib inflateEnd error: %s", stream.msg);
fz_free(inbuf);
}
else
{
return fz_throw("unknown compression method (%d)", method);
}
return fz_okay;
}
/*
* Read the central directory in a zip file.
*/
static int
xps_read_zip_dir(xps_context *ctx, int start_offset)
{
int sig;
int offset, count;
int namesize, metasize, commentsize;
int i;
fz_seek(ctx->file, start_offset, 0);
sig = getlong(ctx->file);
if (sig != ZIP_END_OF_CENTRAL_DIRECTORY_SIG)
return fz_throw("wrong zip end of central directory signature (0x%x)", sig);
(void) getshort(ctx->file); /* this disk */
(void) getshort(ctx->file); /* start disk */
(void) getshort(ctx->file); /* entries in this disk */
count = getshort(ctx->file); /* entries in central directory disk */
(void) getlong(ctx->file); /* size of central directory */
offset = getlong(ctx->file); /* offset to central directory */
ctx->zip_count = count;
ctx->zip_table = fz_calloc(count, sizeof(xps_entry));
memset(ctx->zip_table, 0, sizeof(xps_entry) * count);
fz_seek(ctx->file, offset, 0);
for (i = 0; i < count; i++)
{
sig = getlong(ctx->file);
if (sig != ZIP_CENTRAL_DIRECTORY_SIG)
return fz_throw("wrong zip central directory signature (0x%x)", sig);
(void) getshort(ctx->file); /* version made by */
(void) getshort(ctx->file); /* version to extract */
(void) getshort(ctx->file); /* general */
(void) getshort(ctx->file); /* method */
(void) getshort(ctx->file); /* last mod file time */
(void) getshort(ctx->file); /* last mod file date */
(void) getlong(ctx->file); /* crc-32 */
ctx->zip_table[i].csize = getlong(ctx->file);
ctx->zip_table[i].usize = getlong(ctx->file);
namesize = getshort(ctx->file);
metasize = getshort(ctx->file);
commentsize = getshort(ctx->file);
(void) getshort(ctx->file); /* disk number start */
(void) getshort(ctx->file); /* int file atts */
(void) getlong(ctx->file); /* ext file atts */
ctx->zip_table[i].offset = getlong(ctx->file);
ctx->zip_table[i].name = fz_malloc(namesize + 1);
fz_read(ctx->file, (unsigned char*)ctx->zip_table[i].name, namesize);
ctx->zip_table[i].name[namesize] = 0;
fz_seek(ctx->file, metasize, 1);
fz_seek(ctx->file, commentsize, 1);
}
qsort(ctx->zip_table, count, sizeof(xps_entry), xps_compare_entries);
return fz_okay;
}
static int
xps_find_and_read_zip_dir(xps_context *ctx)
{
unsigned char buf[512];
int file_size, back, maxback;
int i, n;
fz_seek(ctx->file, 0, SEEK_END);
file_size = fz_tell(ctx->file);
maxback = MIN(file_size, 0xFFFF + sizeof buf);
back = MIN(maxback, sizeof buf);
while (back < maxback)
{
fz_seek(ctx->file, file_size - back, 0);
n = fz_read(ctx->file, buf, sizeof buf);
if (n < 0)
return fz_throw("cannot read end of central directory");
for (i = n - 4; i > 0; i--)
if (!memcmp(buf + i, "PK\5\6", 4))
return xps_read_zip_dir(ctx, file_size - back + i);
back += sizeof buf - 4;
}
return fz_throw("cannot find end of central directory");
}
/*
* Read and interleave split parts from a ZIP file.
*/
static xps_part *
xps_read_zip_part(xps_context *ctx, char *partname)
{
char buf[2048];
xps_entry *ent;
xps_part *part;
int count, size, offset, i;
char *name;
name = partname;
if (name[0] == '/')
name ++;
/* All in one piece */
ent = xps_find_zip_entry(ctx, name);
if (ent)
{
part = xps_new_part(ctx, partname, ent->usize);
xps_read_zip_entry(ctx, ent, part->data);
return part;
}
/* Count the number of pieces and their total size */
count = 0;
size = 0;
while (1)
{
sprintf(buf, "%s/[%d].piece", name, count);
ent = xps_find_zip_entry(ctx, buf);
if (!ent)
{
sprintf(buf, "%s/[%d].last.piece", name, count);
ent = xps_find_zip_entry(ctx, buf);
}
if (!ent)
break;
count ++;
size += ent->usize;
}
/* Inflate the pieces */
if (count)
{
part = xps_new_part(ctx, partname, size);
offset = 0;
for (i = 0; i < count; i++)
{
if (i < count - 1)
sprintf(buf, "%s/[%d].piece", name, i);
else
sprintf(buf, "%s/[%d].last.piece", name, i);
ent = xps_find_zip_entry(ctx, buf);
xps_read_zip_entry(ctx, ent, part->data + offset);
offset += ent->usize;
}
return part;
}
return NULL;
}
/*
* Read and interleave split parts from files in the directory.
*/
static xps_part *
xps_read_dir_part(xps_context *ctx, char *name)
{
char buf[2048];
xps_part *part;
FILE *file;
int count, size, offset, i, n;
fz_strlcpy(buf, ctx->directory, sizeof buf);
fz_strlcat(buf, name, sizeof buf);
/* All in one piece */
file = fopen(buf, "rb");
if (file)
{
fseek(file, 0, SEEK_END);
size = ftell(file);
fseek(file, 0, SEEK_SET);
part = xps_new_part(ctx, name, size);
fread(part->data, 1, size, file);
fclose(file);
return part;
}
/* Count the number of pieces and their total size */
count = 0;
size = 0;
while (1)
{
sprintf(buf, "%s%s/[%d].piece", ctx->directory, name, count);
file = fopen(buf, "rb");
if (!file)
{
sprintf(buf, "%s%s/[%d].last.piece", ctx->directory, name, count);
file = fopen(buf, "rb");
}
if (!file)
break;
count ++;
fseek(file, 0, SEEK_END);
size += ftell(file);
fclose(file);
}
/* Inflate the pieces */
if (count)
{
part = xps_new_part(ctx, name, size);
offset = 0;
for (i = 0; i < count; i++)
{
if (i < count - 1)
sprintf(buf, "%s%s/[%d].piece", ctx->directory, name, i);
else
sprintf(buf, "%s%s/[%d].last.piece", ctx->directory, name, i);
file = fopen(buf, "rb");
n = fread(part->data + offset, 1, size - offset, file);
offset += n;
fclose(file);
}
return part;
}
return NULL;
}
xps_part *
xps_read_part(xps_context *ctx, char *partname)
{
if (ctx->directory)
return xps_read_dir_part(ctx, partname);
return xps_read_zip_part(ctx, partname);
}
static int
xps_open_directory(xps_context **ctxp, char *directory)
{
xps_context *ctx;
int code;
ctx = fz_malloc(sizeof(xps_context));
memset(ctx, 0, sizeof(xps_context));
ctx->directory = fz_strdup(directory);
code = xps_read_page_list(ctx);
if (code)
{
xps_free_context(ctx);
return fz_rethrow(code, "cannot read page list");
}
*ctxp = ctx;
return fz_okay;
}
int
xps_open_stream(xps_context **ctxp, fz_stream *file)
{
xps_context *ctx;
int code;
ctx = fz_malloc(sizeof(xps_context));
memset(ctx, 0, sizeof(xps_context));
ctx->file = fz_keep_stream(file);
code = xps_find_and_read_zip_dir(ctx);
if (code < 0)
{
xps_free_context(ctx);
return fz_rethrow(code, "cannot read zip central directory");
}
code = xps_read_page_list(ctx);
if (code)
{
xps_free_context(ctx);
return fz_rethrow(code, "cannot read page list");
}
*ctxp = ctx;
return fz_okay;
}
int
xps_open_file(xps_context **ctxp, char *filename)
{
char buf[2048];
fz_stream *file;
char *p;
int code;
if (strstr(filename, "/_rels/.rels") || strstr(filename, "\\_rels\\.rels"))
{
fz_strlcpy(buf, filename, sizeof buf);
p = strstr(buf, "/_rels/.rels");
if (!p)
p = strstr(buf, "\\_rels\\.rels");
*p = 0;
return xps_open_directory(ctxp, buf);
}
file = fz_open_file(filename);
if (!file)
return fz_throw("cannot open file '%s': %s", filename, strerror(errno));
code = xps_open_stream(ctxp, file);
fz_close(file);
if (code)
return fz_rethrow(code, "cannot load document '%s'", filename);
return fz_okay;
}
void
xps_free_context(xps_context *ctx)
{
xps_font_cache *font, *next;
int i;
if (ctx->file)
fz_close(ctx->file);
for (i = 0; i < ctx->zip_count; i++)
fz_free(ctx->zip_table[i].name);
fz_free(ctx->zip_table);
font = ctx->font_table;
while (font)
{
next = font->next;
fz_drop_font(font->font);
fz_free(font->name);
fz_free(font);
font = next;
}
xps_free_page_list(ctx);
fz_free(ctx->start_part);
fz_free(ctx->directory);
fz_free(ctx);
}