forked from KolibriOS/kolibrios
updf: remove xps support as non-used
git-svn-id: svn://kolibrios.org@7621 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
parent
42c754192f
commit
12dca64514
@ -7,7 +7,7 @@ GEN := generated
|
||||
|
||||
default: all
|
||||
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
|
||||
|
||||
#include Makerules
|
||||
@ -49,31 +49,26 @@ $(OUT)/%.o : draw/%.c fitz/fitz.h | $(OUT)
|
||||
$(CC_CMD)
|
||||
$(OUT)/%.o : pdf/%.c fitz/fitz.h pdf/mupdf.h | $(OUT)
|
||||
$(CC_CMD)
|
||||
$(OUT)/%.o : xps/%.c fitz/fitz.h xps/muxps.h | $(OUT)
|
||||
$(CC_CMD)
|
||||
$(OUT)/%.o : apps/%.c fitz/fitz.h pdf/mupdf.h xps/muxps.h | $(OUT)
|
||||
$(OUT)/%.o : apps/%.c fitz/fitz.h pdf/mupdf.h | $(OUT)
|
||||
$(CC_CMD)
|
||||
$(OUT)/%.o : scripts/%.c | $(OUT)
|
||||
$(CC_CMD)
|
||||
|
||||
.PRECIOUS : $(OUT)/%.o # Keep intermediates from chained rules
|
||||
|
||||
# --- Fitz, MuPDF and MuXPS libraries ---
|
||||
# --- Fitz and MuPDF libraries ---
|
||||
|
||||
FITZ_LIB := $(OUT)/libfitz.a
|
||||
MUPDF_LIB := $(OUT)/libmupdf.a
|
||||
MUXPS_LIB := $(OUT)/libmuxps.a
|
||||
|
||||
FITZ_SRC := $(notdir $(wildcard fitz/*.c draw/*.c))
|
||||
MUPDF_SRC := $(notdir $(wildcard pdf/*.c))
|
||||
MUXPS_SRC := $(notdir $(wildcard xps/*.c))
|
||||
|
||||
$(FITZ_LIB) : $(addprefix $(OUT)/, $(FITZ_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)
|
||||
@ echo MuPDF/XPS and underlying libraries built
|
||||
libs: $(MUPDF_LIB) $(FITZ_LIB) $(THIRD_LIBS)
|
||||
@ echo MuPDF and underlying libraries built
|
||||
|
||||
# --- Generated CMAP and FONT files ---
|
||||
|
||||
@ -121,40 +116,14 @@ $(OUT)/cmapdump.o : pdf/pdf_cmap.c pdf/pdf_cmap_parse.c
|
||||
# --- Tools and Apps ---
|
||||
|
||||
PDF_APPS := $(addprefix $(OUT)/, pdfdraw pdfclean pdfextract pdfinfo pdfshow)
|
||||
XPS_APPS := $(addprefix $(OUT)/, xpsdraw)
|
||||
|
||||
$(PDF_APPS) : $(MUPDF_LIB) $(FITZ_LIB) $(THIRD_LIBS)
|
||||
$(XPS_APPS) : $(MUXPS_LIB) $(FITZ_LIB) $(THIRD_LIBS)
|
||||
|
||||
MUPDF := $(OUT)/mupdf
|
||||
$(MUPDF) : $(MUXPS_LIB) $(MUPDF_LIB) $(FITZ_LIB) $(THIRD_LIBS)
|
||||
$(MUPDF) : $(MUPDF_LIB) $(FITZ_LIB) $(THIRD_LIBS)
|
||||
ifeq "$(NOX11)" ""
|
||||
$(MUPDF) : $(addprefix $(OUT)/, kos_main.o pdfapp.o)
|
||||
$(LINK_CMD)
|
||||
endif
|
||||
|
||||
# --- Install ---
|
||||
|
||||
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
|
||||
all: $(THIRD_LIBS) $(FITZ_LIB) $(PDF_APPS) $(MUPDF)
|
||||
|
@ -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
|
@ -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
|
@ -2,7 +2,6 @@
|
||||
#define _WIN32
|
||||
#include "fitz.h"
|
||||
#include "mupdf.h"
|
||||
#include "muxps.h"
|
||||
#include "pdfapp.h"
|
||||
#include "icons/allbtns.h"
|
||||
#include "kolibri.c"
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "fitz.h"
|
||||
#include "mupdf.h"
|
||||
#include "muxps.h"
|
||||
#include "pdfapp.h"
|
||||
|
||||
#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 previous search result\n"
|
||||
"c\t\t-- toggle between color and grayscale\n"
|
||||
; */
|
||||
;
|
||||
*/
|
||||
}
|
||||
|
||||
void pdfapp_init(pdfapp_t *app)
|
||||
@ -172,27 +172,8 @@ __menuet__debug_out("Page counter\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)
|
||||
{
|
||||
if (strstr(filename, ".xps") || strstr(filename, ".XPS") || strstr(filename, ".rels"))
|
||||
pdfapp_open_xps(app, filename, fd);
|
||||
else
|
||||
pdfapp_open_pdf(app, filename, fd);
|
||||
|
||||
app->cache = fz_new_glyph_cache();
|
||||
@ -241,12 +222,6 @@ void pdfapp_close(pdfapp_t *app)
|
||||
app->xref = NULL;
|
||||
}
|
||||
|
||||
if (app->xps)
|
||||
{
|
||||
xps_free_context(app->xps);
|
||||
app->xps = NULL;
|
||||
}
|
||||
|
||||
fz_flush_warnings();
|
||||
}
|
||||
|
||||
@ -333,34 +308,6 @@ static void pdfapp_loadpage_pdf(pdfapp_t *app)
|
||||
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)
|
||||
{
|
||||
char buf[256];
|
||||
@ -383,8 +330,6 @@ static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repai
|
||||
|
||||
if (app->xref)
|
||||
pdfapp_loadpage_pdf(app);
|
||||
if (app->xps)
|
||||
pdfapp_loadpage_xps(app);
|
||||
|
||||
/* Zero search hit position */
|
||||
app->hit = -1;
|
||||
@ -399,9 +344,9 @@ static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repai
|
||||
|
||||
if (drawpage)
|
||||
{
|
||||
// sprintf(buf, "%s - %d/%d (%d dpi)", app->doctitle,
|
||||
// app->pageno, app->pagecount, app->resolution);
|
||||
// wintitle(app, buf);
|
||||
sprintf(buf, "%s - %d/%d (%d dpi)", app->doctitle,
|
||||
app->pageno, app->pagecount, app->resolution);
|
||||
wintitle(app, buf);
|
||||
|
||||
ctm = pdfapp_viewctm(app);
|
||||
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)
|
||||
{
|
||||
//__menuet__debug_out ("SHRINK\n");
|
||||
__menuet__debug_out ("SHRINK\n");
|
||||
int w = app->image->w;
|
||||
int h = app->image->h;
|
||||
if (app->winw == w)
|
||||
|
@ -32,7 +32,6 @@ struct pdfapp_s
|
||||
char *doctitle;
|
||||
pdf_xref *xref;
|
||||
pdf_outline *outline;
|
||||
xps_context *xps;
|
||||
|
||||
int pagecount;
|
||||
fz_glyph_cache *cache;
|
||||
|
@ -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;
|
||||
}
|
@ -619,7 +619,7 @@ FT_BEGIN_HEADER
|
||||
#else /* current sources say */
|
||||
#define TT_MS_LANGID_CROATIAN_BOSNIA_HERZEGOVINA 0x101a
|
||||
#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. */
|
||||
#define TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_LATIN 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_HINDI_INDIA 0x0439
|
||||
#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_SWEDEN 0x083b
|
||||
#define TT_MS_LANGID_SAMI_NORTHERN_FINLAND 0x0C3b
|
||||
@ -751,7 +751,7 @@ FT_BEGIN_HEADER
|
||||
#define TT_MS_LANGID_TAMAZIGHT_MOROCCO_LATIN 0x085f
|
||||
/* Missing a LCID for Tifinagh script */
|
||||
#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 */
|
||||
#define TT_MS_LANGID_KASHMIRI_SASIA 0x0860
|
||||
/* ... 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_PERU 0x0c6b
|
||||
#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 \
|
||||
TT_MS_LANGID_SEPEDI_SOUTH_AFRICA
|
||||
/* language codes 0x046d, 0x046e and 0x046f are (still) unknown. */
|
||||
|
@ -1,5 +0,0 @@
|
||||
#include <math.h>
|
||||
|
||||
long long int lrintf(float x) {
|
||||
return floor(x);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
@ -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];
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user