summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2013-07-17 17:32:12 +0100
committerRobin Watts <robin.watts@artifex.com>2013-07-19 19:56:27 +0100
commit42eb247ea0c69ba8cc249b1bb44fafcdcbdd2621 (patch)
tree6fde9d99a9551faa8abc6e0d4d3aa24db5824e4f
parent90d1d2cf603ba9d61e8160c0ba12325cd8249034 (diff)
downloadmupdf-42eb247ea0c69ba8cc249b1bb44fafcdcbdd2621.tar.xz
Add mupdf-curl app
Windows and X11. Allows files to be fetched and displayed as they are downloaded both with and without linearization, using hints if available.
-rw-r--r--Makefile14
-rw-r--r--Makethird137
-rw-r--r--docs/progressive.txt66
-rw-r--r--platform/win32/mupdf-curl.vcproj421
-rw-r--r--platform/win32/mupdf.sln39
-rw-r--r--platform/x11/curl_stream.c525
-rw-r--r--platform/x11/curl_stream.h7
-rw-r--r--platform/x11/jstest_main.c4
-rw-r--r--platform/x11/pdfapp.c110
-rw-r--r--platform/x11/pdfapp.h7
-rw-r--r--platform/x11/win_main.c24
-rw-r--r--platform/x11/x11_main.c24
12 files changed, 1320 insertions, 58 deletions
diff --git a/Makefile b/Makefile
index 6f2dbbc1..2e72fbe4 100644
--- a/Makefile
+++ b/Makefile
@@ -142,7 +142,11 @@ $(OUT)/%.o : scripts/%.c | $(OUT)
$(CC_CMD)
$(OUT)/platform/x11/%.o : platform/x11/%.c | $(ALL_DIR)
- $(CC_CMD) $(X11_CFLAGS)
+ $(CC_CMD) $(X11_CFLAGS) $(CURL_CFLAGS)
+
+$(OUT)/platform/x11/curl/%.o : platform/x11/%.c | $(ALL_DIR)
+ mkdir -p $(OUT)/platform/x11/curl
+ $(CC_CMD) $(X11_CFLAGS) $(CURL_CFLAGS) -DHAVE_CURL
.PRECIOUS : $(OUT)/%.o # Keep intermediates from chained rules
@@ -234,6 +238,11 @@ MUVIEW_X11 := $(OUT)/mupdf-x11
$(MUVIEW_X11) : $(MUPDF_LIB) $(MUPDF_JS_NONE_LIB) $(THIRD_LIBS)
$(MUVIEW_X11) : $(addprefix $(OUT)/platform/x11/, x11_main.o x11_image.o pdfapp.o)
$(LINK_CMD) $(X11_LIBS)
+
+MUVIEW_X11_CURL := $(OUT)/mupdf-curl
+$(MUVIEW_X11_CURL) : $(MUPDF_LIB) $(MUPDF_JS_NONE_LIB) $(THIRD_LIBS) $(CURL_LIB)
+$(MUVIEW_X11_CURL) : $(addprefix $(OUT)/platform/x11/curl/, x11_main.o x11_image.o pdfapp.o curl_stream.o)
+ $(LINK_CMD) $(X11_LIBS) $(CURL_LIBS)
endif
ifeq "$(V8_PRESENT)" "yes"
@@ -247,8 +256,9 @@ endif
MUVIEW := $(MUVIEW_X11)
MUVIEW_V8 := $(MUVIEW_X11_V8)
+MUVIEW_CURL := $(MUVIEW_X11_CURL)
-INSTALL_APPS := $(MUDRAW) $(MUTOOL) $(MUVIEW) $(MUJSTEST_V8) $(MUVIEW_V8)
+INSTALL_APPS := $(MUDRAW) $(MUTOOL) $(MUVIEW) $(MUJSTEST_V8) $(MUVIEW_V8) $(MUVIEW_X11_CURL)
# --- Format man pages ---
diff --git a/Makethird b/Makethird
index 8ae8e625..4a486ebd 100644
--- a/Makethird
+++ b/Makethird
@@ -12,6 +12,7 @@ JPEG_DIR := thirdparty/jpeg
OPENJPEG_DIR := thirdparty/openjpeg
OPENSSL_DIR := thirdparty/openssl
ZLIB_DIR := thirdparty/zlib
+CURL_DIR := thirdparty/curl
# --- V8 ---
#
@@ -310,3 +311,139 @@ else
X11_CFLAGS :=
X11_LIBS :=
endif
+
+# --- cURL ---
+
+ifneq "$(wildcard $(CURL_DIR)/README)" ""
+
+CURL_LIB := $(OUT)/libcurl.a
+CURL_OUT := $(OUT)/curl
+CURL_SRC := \
+ amigaos.c \
+ asyn-ares.c \
+ asyn-thread.c \
+ axtls.c \
+ base64.c \
+ bundles.c \
+ conncache.c \
+ connect.c \
+ content_encoding.c \
+ cookie.c \
+ curl_addrinfo.c \
+ curl_darwinssl.c \
+ curl_fnmatch.c \
+ curl_gethostname.c \
+ curl_gssapi.c \
+ curl_memrchr.c \
+ curl_multibyte.c \
+ curl_ntlm.c \
+ curl_ntlm_core.c \
+ curl_ntlm_msgs.c \
+ curl_ntlm_wb.c \
+ curl_rand.c \
+ curl_rtmp.c \
+ curl_sasl.c \
+ curl_schannel.c \
+ curl_sspi.c \
+ curl_threads.c \
+ cyassl.c \
+ dict.c \
+ easy.c \
+ escape.c \
+ file.c \
+ fileinfo.c \
+ formdata.c \
+ ftp.c \
+ ftplistparser.c \
+ getenv.c \
+ getinfo.c \
+ gopher.c \
+ gtls.c \
+ hash.c \
+ hmac.c \
+ hostasyn.c \
+ hostcheck.c \
+ hostip.c \
+ hostip4.c \
+ hostip6.c \
+ hostsyn.c \
+ http.c \
+ http_chunks.c \
+ http_digest.c \
+ http_negotiate.c \
+ http_negotiate_sspi.c \
+ http_proxy.c \
+ idn_win32.c \
+ if2ip.c \
+ imap.c \
+ inet_ntop.c \
+ inet_pton.c \
+ krb4.c \
+ krb5.c \
+ ldap.c \
+ llist.c \
+ md4.c \
+ md5.c \
+ memdebug.c \
+ mprintf.c \
+ multi.c \
+ netrc.c \
+ non-ascii.c \
+ nonblock.c \
+ nss.c \
+ openldap.c \
+ parsedate.c \
+ pingpong.c \
+ pipeline.c \
+ polarssl.c \
+ polarssl_threadlock.c \
+ pop3.c \
+ progress.c \
+ qssl.c \
+ rawstr.c \
+ rtsp.c \
+ security.c \
+ select.c \
+ sendf.c \
+ share.c \
+ slist.c \
+ smtp.c \
+ socks.c \
+ socks_gssapi.c \
+ speedcheck.c \
+ splay.c \
+ ssh.c \
+ sslgen.c \
+ ssluse.c \
+ strdup.c \
+ strequal.c \
+ strerror.c \
+ strtok.c \
+ strtoofft.c \
+ telnet.c \
+ tftp.c \
+ timeval.c \
+ transfer.c \
+ url.c \
+ version.c \
+ warnless.c \
+ wildcard.c \
+
+
+$(CURL_LIB): $(addprefix $(CURL_OUT)/, $(CURL_SRC:%.c=%.o))
+
+$(CURL_OUT):
+ $(MKDIR_CMD)
+
+CRL_CFLAGS := -DHAVE_CONFIG_H -DBUILDING_LIBCURL -DCURL_STATICLIB \
+ -DCURL_DISABLE_LDAP -I$(CURL_DIR)/include
+
+$(CURL_OUT)/%.o: $(CURL_DIR)/lib/%.c | $(CURL_OUT)
+ $(CC_CMD) $(CRL_CFLAGS)
+
+CURL_CFLAGS := -I$(CURL_DIR)/include
+CURL_LIBS := -lpthread -lrt
+else
+CURL_CFLAGS := $(SYS_CURL_CFLAGS)
+CURL_LIBS := $(SYS_CURL_LIBS)
+endif
diff --git a/docs/progressive.txt b/docs/progressive.txt
index e626c26f..a39e70d9 100644
--- a/docs/progressive.txt
+++ b/docs/progressive.txt
@@ -7,11 +7,15 @@ What is progressive loading?
The idea of progressive loading is that as you download a PDF file
into a browser, you can display the pages as they appear.
-There are 2 mechanisms by which this can be achieved. The first
-relies on the file being "linearized", the second relies on the
-caller of MuPDF having fine control over the http fetch and on
+MuPDF can make use of 2 different mechanisms to achieve this. The
+first relies on the file being "linearized", the second relies on
+the caller of MuPDF having fine control over the http fetch and on
the server supporting byte-range fetches.
+For optimum performance a file should be both linearized and be
+available over a byte-range supporting link, but benefits can still
+be had with either one of these alone.
+
Progressive download using "linearized" files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -24,9 +28,16 @@ document downloading, whereupon subsequent pages will become
available. Adobe also refers to these as "Optimized for fast web
view" or "Web Optimized".
-MuPDF can actually slightly outperform this by displaying the first
-page quickly, and then providing 'incomplete' renderings of
-subsequent pages, as more and more resources are gradually delivered.
+In fact, the standard outlines (poorly) a mechanism by which 'hints'
+can be included that enable the subsequent pages to be found within
+the file too. Unfortunately this is very poorly supported with
+many tools, and so the hints have to be treated with suspicion.
+
+MuPDF will attempt to use hints if they are available, but will also
+use a linear search of the file to discover pages if not. This means
+that the first page will be displayed quickly, and then subsequent
+ones will appear with 'incomplete' renderings that improve over time
+as more and more resources are gradually delivered.
Essentially the file starts with a slightly modified header, and the
first object in the file is a special one (the linearization object)
@@ -44,13 +55,17 @@ resources until after all the unshared page objects have been
sent.]
-The Hint Stream, and why we don't use it.
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The Hint Stream
+~~~~~~~~~~~~~~~
Adobe intended Hint Stream to be useful to facilitate the display
of subsequent pages, but it has never used it. Consequently you
-can't trust people to write it properly. Consequently no one
-actually uses it. Consequently we should make do without it too.
+can't trust people to write it properly - indeed Adobe outputs
+something that doesn't quite conform to the spec.
+
+Consequently very few people actually use it. MuPDF will use it
+after sanity checking the values, and should cope with illegal/
+incorrect values.
So how does MuPDF handle progressive loading?
@@ -250,13 +265,13 @@ a server.
+ We consider the file as an (initially empty) buffer which we are
filling by making requests. In order to ensure that we make
- maximum use of our download link, we should ensure that whenever
+ maximum use of our download link, we ensure that whenever
one request finishes, we immediately launch another. Further, to
avoid the overheads for the request/response headers being too
large, we may want to divide the file into 'chunks', perhaps 4 or 32k
in size.
- + We can then imagine a receiver process that sits there in a loop
+ + We can then have a receiver process that sits there in a loop
requesting chunks to fill this buffer. In the absence of
any other impetus the receiver should request the next 'chunk'
of data from the file that it does not yet have, following the last
@@ -264,26 +279,7 @@ a server.
the file, but this will move around based on the requests made of
the progressive stream.
- + We attempt to open the file, and MuPDF will read from the progressive
- stream. It will first read the PDF file header (i.e. it will read from
- within the area of the file we have already requested). It will then
- attempt to seek to the end of the file to read the trailer. The
- stream then has a choice; it can choose to block until such time as
- data arrives (unlikely to be satisfactory as this blocks all other
- MuPDF operations), or it can throw an FZ_ERROR_TRYLATER error.
-
- + Whichever of these it chooses to do, it knows that the next 'block'
- of the file that is required will be at the end of the file, so it
- can set the desired fill pointer in the receiver process to arrange
- that the next block requested will be that at the end of the file.
-
- + When this data arrives, it can either unblock and continue, or
- retry the MuPDF call that exited with the FZ_ERROR_TRYLATER error.
-
- + When the file trailer has been read, the file will then attempt to
- seek and read the xref information. Again this will cause the http
- receiver to request that area of the file next.
-
- + Accordingly, the file will be read using 'random access', where
- the stream is in control of either blocking or asking operations
- to retry later.
+ + Whenever MuPDF attempts to read from the stream, we check to see if
+ we have data for this area of the file already. If we do, we can
+ return it. If not, we remember this as the next "fill point" for our
+ receiver process and throw an FZ_ERROR_TRYLATER error.
diff --git a/platform/win32/mupdf-curl.vcproj b/platform/win32/mupdf-curl.vcproj
new file mode 100644
index 00000000..91217d8b
--- /dev/null
+++ b/platform/win32/mupdf-curl.vcproj
@@ -0,0 +1,421 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="mupdf-curl"
+ ProjectGUID="{27B53E5C-ACAB-423C-854E-BECE56D73544}"
+ RootNamespace="mupdf"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)\$(ProjectName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\include,..\..\thirdparty\curl\include"
+ PreprocessorDefinitions="FT2_BUILD_LIBRARY;OPJ_STATIC;DEBUG=1;HAVE_CURL=1"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ GenerateDebugInformation="true"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)\$(ProjectName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="..\..\include,..\..\thirdparty\curl\include"
+ PreprocessorDefinitions="HAVE_CURL=1"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ GenerateDebugInformation="true"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Memento|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)\$(ProjectName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\include,..\..\thirdparty\curl\include"
+ PreprocessorDefinitions="FT2_BUILD_LIBRARY;OPJ_STATIC;MEMENTO=1;DEBUG=1;HAVE_CURL=1"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ GenerateDebugInformation="true"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="DebugOpenssl|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\include,..\..\thirdparty\curl\include"
+ PreprocessorDefinitions="FT2_BUILD_LIBRARY;OPJ_STATIC;DEBUG=1;HAVE_OPENSSL;HAVE_CURL=1"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ AdditionalLibraryDirectories="..\..\thirdparty\openssl\lib"
+ GenerateDebugInformation="true"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="ReleaseOpenssl|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="..\..\include,..\..\thirdparty\curl\include"
+ PreprocessorDefinitions="HAVE_OPENSSL;HAVE_CURL=1"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ AdditionalLibraryDirectories="..\..\thirdparty\openssl\lib"
+ GenerateDebugInformation="true"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath="..\x11\curl_stream.c"
+ >
+ </File>
+ <File
+ RelativePath="..\x11\curl_stream.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\platform\x11\pdfapp.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\platform\x11\pdfapp.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\platform\x11\win_main.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\platform\x11\win_res.rc"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/platform/win32/mupdf.sln b/platform/win32/mupdf.sln
index a179a008..b8de7e2a 100644
--- a/platform/win32/mupdf.sln
+++ b/platform/win32/mupdf.sln
@@ -3,8 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mupdf", "mupdf.vcproj", "{E74F29F0-FA43-4ADC-B92C-6AFA08E4A417}"
ProjectSection(ProjectDependencies) = postProject
- {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C} = {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C}
{EC81A9F3-88A6-4170-B7B4-C41CB789A7F6} = {EC81A9F3-88A6-4170-B7B4-C41CB789A7F6}
+ {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C} = {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libthirdparty", "libthirdparty.vcproj", "{5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C}"
@@ -16,14 +16,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmupdf", "libmupdf.vcproj
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mudraw", "mudraw.vcproj", "{0B51171B-B10E-4EAC-8FFA-19226A1828A3}"
ProjectSection(ProjectDependencies) = postProject
- {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C} = {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C}
{EC81A9F3-88A6-4170-B7B4-C41CB789A7F6} = {EC81A9F3-88A6-4170-B7B4-C41CB789A7F6}
+ {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C} = {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mutool", "mutool.vcproj", "{00811970-815B-4F64-BC9D-219078B1F3AA}"
ProjectSection(ProjectDependencies) = postProject
- {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C} = {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C}
{EC81A9F3-88A6-4170-B7B4-C41CB789A7F6} = {EC81A9F3-88A6-4170-B7B4-C41CB789A7F6}
+ {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C} = {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "generated", "generated.vcproj", "{A5053AA7-02E5-4903-B596-04F17AEB1526}"
@@ -35,14 +35,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmupdf-js-v8", "libmupdf-
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mupdf-v8", "mupdf-v8.vcproj", "{9035A4F3-4219-45A5-985D-FBF4D9609713}"
ProjectSection(ProjectDependencies) = postProject
- {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D} = {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D}
{5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C} = {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C}
+ {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D} = {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mujstest-v8", "mujstest-v8.vcproj", "{21E28758-E4D2-4B84-8EC5-B631CEE66B30}"
ProjectSection(ProjectDependencies) = postProject
- {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C} = {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C}
{2E5DAFDB-A060-4011-B760-32F6A3A4BC9D} = {2E5DAFDB-A060-4011-B760-32F6A3A4BC9D}
+ {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C} = {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmupdf-js-none", "libmupdf-js-none.vcproj", "{EC81A9F3-88A6-4170-B7B4-C41CB789A7F6}"
@@ -50,6 +50,15 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmupdf-js-none", "libmupd
{5F615F91-DFF8-4F05-BF48-6222B7D86519} = {5F615F91-DFF8-4F05-BF48-6222B7D86519}
EndProjectSection
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcurl", "..\..\thirdparty\curl\vs\vc8\lib\vc8libcurl.vcproj", "{87EE9DA4-DE1E-4448-8324-183C98DCA588}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mupdf-curl", "mupdf-curl.vcproj", "{27B53E5C-ACAB-423C-854E-BECE56D73544}"
+ ProjectSection(ProjectDependencies) = postProject
+ {87EE9DA4-DE1E-4448-8324-183C98DCA588} = {87EE9DA4-DE1E-4448-8324-183C98DCA588}
+ {EC81A9F3-88A6-4170-B7B4-C41CB789A7F6} = {EC81A9F3-88A6-4170-B7B4-C41CB789A7F6}
+ {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C} = {5EDCF4FD-0291-4FB9-8D96-D58957CA5E3C}
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -159,6 +168,26 @@ Global
{EC81A9F3-88A6-4170-B7B4-C41CB789A7F6}.Release|Win32.Build.0 = Release|Win32
{EC81A9F3-88A6-4170-B7B4-C41CB789A7F6}.ReleaseOpenssl|Win32.ActiveCfg = ReleaseOpenssl|Win32
{EC81A9F3-88A6-4170-B7B4-C41CB789A7F6}.ReleaseOpenssl|Win32.Build.0 = ReleaseOpenssl|Win32
+ {87EE9DA4-DE1E-4448-8324-183C98DCA588}.Debug|Win32.ActiveCfg = Debug|Win32
+ {87EE9DA4-DE1E-4448-8324-183C98DCA588}.Debug|Win32.Build.0 = Debug|Win32
+ {87EE9DA4-DE1E-4448-8324-183C98DCA588}.DebugOpenssl|Win32.ActiveCfg = Debug|Win32
+ {87EE9DA4-DE1E-4448-8324-183C98DCA588}.DebugOpenssl|Win32.Build.0 = Debug|Win32
+ {87EE9DA4-DE1E-4448-8324-183C98DCA588}.Memento|Win32.ActiveCfg = Memento|Win32
+ {87EE9DA4-DE1E-4448-8324-183C98DCA588}.Memento|Win32.Build.0 = Memento|Win32
+ {87EE9DA4-DE1E-4448-8324-183C98DCA588}.Release|Win32.ActiveCfg = Release|Win32
+ {87EE9DA4-DE1E-4448-8324-183C98DCA588}.Release|Win32.Build.0 = Release|Win32
+ {87EE9DA4-DE1E-4448-8324-183C98DCA588}.ReleaseOpenssl|Win32.ActiveCfg = Release|Win32
+ {87EE9DA4-DE1E-4448-8324-183C98DCA588}.ReleaseOpenssl|Win32.Build.0 = Release|Win32
+ {27B53E5C-ACAB-423C-854E-BECE56D73544}.Debug|Win32.ActiveCfg = Debug|Win32
+ {27B53E5C-ACAB-423C-854E-BECE56D73544}.Debug|Win32.Build.0 = Debug|Win32
+ {27B53E5C-ACAB-423C-854E-BECE56D73544}.DebugOpenssl|Win32.ActiveCfg = DebugOpenssl|Win32
+ {27B53E5C-ACAB-423C-854E-BECE56D73544}.DebugOpenssl|Win32.Build.0 = DebugOpenssl|Win32
+ {27B53E5C-ACAB-423C-854E-BECE56D73544}.Memento|Win32.ActiveCfg = Memento|Win32
+ {27B53E5C-ACAB-423C-854E-BECE56D73544}.Memento|Win32.Build.0 = Memento|Win32
+ {27B53E5C-ACAB-423C-854E-BECE56D73544}.Release|Win32.ActiveCfg = Release|Win32
+ {27B53E5C-ACAB-423C-854E-BECE56D73544}.Release|Win32.Build.0 = Release|Win32
+ {27B53E5C-ACAB-423C-854E-BECE56D73544}.ReleaseOpenssl|Win32.ActiveCfg = ReleaseOpenssl|Win32
+ {27B53E5C-ACAB-423C-854E-BECE56D73544}.ReleaseOpenssl|Win32.Build.0 = ReleaseOpenssl|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/platform/x11/curl_stream.c b/platform/x11/curl_stream.c
new file mode 100644
index 00000000..3a0de68c
--- /dev/null
+++ b/platform/x11/curl_stream.c
@@ -0,0 +1,525 @@
+/* Simple example fz_stream implementation using curl */
+
+#include "mupdf/fitz.h"
+#include "curl_stream.h"
+
+#define CURL_STATICLIB
+#include <curl/curl.h>
+
+#undef DEBUG_BLOCK_FETCHING
+
+#ifdef DEBUG_BLOCK_FETCHING
+#define DEBUG_MESSAGE(A) do { fz_warn A; } while(0)
+#else
+#define DEBUG_MESSAGE(A) do { } while(0)
+#endif
+
+#define BLOCK_SHIFT 12
+#define BLOCK_SIZE (1<<BLOCK_SHIFT)
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include "pthread.h"
+#include <ctype.h>
+#endif
+
+typedef struct curl_stream_state_s curl_stream_state;
+
+struct curl_stream_state_s
+{
+ fz_context *ctx;
+ CURL *handle;
+ char *filename;
+ int data_arrived;
+ int content_length; /* As returned by curl. -1 for unknown. */
+ int total_length; /* As obtained from the Content-Range header. */
+ int buffer_max;
+ int buffer_fill;
+ unsigned char *buffer;
+ int map_length;
+ unsigned char *map;
+ int fill_point; /* The next file offset we will fetch to */
+ int current_fill_point; /* The current file offset we are fetching to */
+ int complete;
+ int kill_thread;
+ void (*more_data)(void *, int);
+ void *more_data_arg;
+
+#ifdef _WIN32
+ void *thread;
+ DWORD thread_id;
+ HANDLE mutex;
+#else
+ pthread_t thread;
+ pthread_mutex_t mutex;
+#endif
+};
+
+static void fetcher_thread(curl_stream_state *state);
+
+#ifdef _WIN32
+static void
+lock(curl_stream_state *state)
+{
+ WaitForSingleObject(state->mutex, INFINITE);
+}
+
+static void
+unlock(curl_stream_state *state)
+{
+ ReleaseMutex(state->mutex);
+}
+
+static DWORD WINAPI
+win_thread(void *lparam)
+{
+ fetcher_thread((curl_stream_state *)lparam);
+
+ return 0;
+}
+
+#else /* Anything else assumed to be pthreads */
+
+static void
+lock(curl_stream_state *state)
+{
+ pthread_mutex_lock(&state->mutex);
+}
+
+static void
+unlock(curl_stream_state *state)
+{
+ pthread_mutex_unlock(&state->mutex);
+}
+
+
+static void *
+pthread_thread(void *arg)
+{
+ fetcher_thread((curl_stream_state *)arg);
+
+ return NULL;
+}
+#endif
+
+static size_t header_arrived(void *ptr, size_t size, size_t nmemb, void *state_)
+{
+ curl_stream_state *state = (curl_stream_state *)state_;
+
+ if (strncmp(ptr, "Content-Range:", 14) == 0)
+ {
+ char *p = (char *)ptr;
+ int len = nmemb * size;
+ int start, end, total;
+ while (len && !isdigit(*p))
+ p++, len--;
+ start = 0;
+ while (len && isdigit(*p))
+ {
+ start = start*10 + *p-'0';
+ p++, len--;
+ }
+ while (len && !isdigit(*p))
+ p++, len--;
+ end = 0;
+ while (len && isdigit(*p))
+ {
+ end = end*10 + *p-'0';
+ p++, len--;
+ }
+ while (len && !isdigit(*p))
+ p++, len--;
+ total = 0;
+ while (len && isdigit(*p))
+ {
+ total = total*10 + *p-'0';
+ p++, len--;
+ }
+ state->total_length = total;
+ }
+
+ return nmemb * size;
+}
+
+static size_t data_arrived(void *ptr, size_t size, size_t nmemb, void *state_)
+{
+ curl_stream_state *state = (curl_stream_state *)state_;
+ int old_start;
+
+ size *= nmemb;
+
+ if (state->data_arrived == 0)
+ {
+ double d;
+ long response;
+ int len;
+ /* This is the first time data has arrived. If the response
+ * code is 206, then we can do byte requests, and we will
+ * known the total_length from having processed the header
+ * already. */
+ curl_easy_getinfo(state->handle, CURLINFO_RESPONSE_CODE, &response);
+ if (state->total_length && response == 206)
+ {
+ /* We got a range header, and the correct http response
+ * code. We can assume that byte fetches are accepted
+ * and we'll run without progressive mode. */
+ state->content_length = len = state->total_length;
+ state->map_length = (len+BLOCK_SIZE-1)>>BLOCK_SHIFT;
+ state->map = fz_malloc_no_throw(state->ctx, (state->map_length+7)>>3);
+ state->buffer = fz_malloc_no_throw(state->ctx, len);
+ state->buffer_max = len;
+ if (state->map == NULL || state->buffer == NULL)
+ {
+ /* FIXME: Crap error handling! */
+ exit(1);
+ }
+ memset(state->map, 0, (state->map_length+7)>>3);
+ }
+ else
+ {
+ /* So we can't use ByteRanges. Do we at least know the
+ * complete length of the file? */
+ curl_easy_getinfo(state->handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
+ state->content_length = len = (int)d;
+ if (len > 0 && response == 200)
+ {
+ /* Yes. We can run as a progressive file */
+ state->buffer = fz_malloc_no_throw(state->ctx, len);
+ state->buffer_max = len;
+ if (state->buffer == NULL)
+ {
+ /* FIXME: Crap error handling! */
+ exit(1);
+ }
+ }
+ else
+ {
+ /* What a crap server. Won't tell us how big
+ * the file is. We'll have to expand as data
+ * as arrives. */
+ state->content_length = -1;
+ }
+ }
+ state->data_arrived = 1;
+ }
+ if (state->content_length < 0)
+ {
+ int newsize = state->current_fill_point + size;
+ if (newsize > state->buffer_max)
+ {
+ /* Expand the buffer */
+ int new_max = state->buffer_max * 2;
+ if (new_max == 0)
+ new_max = 4096;
+ state->buffer = fz_resize_array_no_throw(state->ctx, state->buffer, new_max, 1);
+ if (state->buffer == NULL)
+ {
+ /* FIXME: Crap error handling! */
+ exit(1);
+ }
+ state->buffer_max = new_max;
+ }
+ }
+
+ DEBUG_MESSAGE((state->ctx, "data arrived: offset=%d len=%d", state->current_fill_point, size));
+ old_start = state->current_fill_point;
+ memcpy(state->buffer + state->current_fill_point, ptr, size);
+ state->current_fill_point += size;
+ if (state->current_fill_point == state->content_length ||
+ (((state->current_fill_point ^ old_start) & ~(BLOCK_SIZE-1)) != 0))
+ {
+ if (state->map)
+ {
+ old_start >>= BLOCK_SHIFT;
+ state->map[old_start>>3] |= 1<<(old_start & 7);
+ }
+ }
+
+ if (state->more_data)
+ state->more_data(state->more_data_arg, 0);
+
+ return size;
+}
+
+#define HAVE_BLOCK(map, num) \
+ (((map)[(num)>>3] & (1<<((num) & 7))) != 0)
+
+static void
+fetch_chunk(curl_stream_state *state)
+{
+ char text[32];
+ int fill, start, end;
+
+ lock(state);
+
+ if (state->kill_thread)
+ {
+ state->complete = 1;
+ unlock(state);
+ return;
+ }
+
+ fill = state->fill_point;
+ if (state->content_length > 0)
+ {
+ /* Find the next block that we haven't got */
+ int map_length = state->map_length;
+ unsigned char *map = state->map;
+ for ( ; (fill < map_length && HAVE_BLOCK(map, fill)); fill++);
+ if (fill == map_length)
+ {
+ for (fill = 0;
+ (fill < map_length && HAVE_BLOCK(map, fill));
+ fill++);
+ if (fill == map_length)
+ {
+ /* We've got it all! */
+ state->complete = 1;
+ state->kill_thread = 1;
+ unlock(state);
+ if (state->more_data)
+ state->more_data(state->more_data_arg, 1);
+ fz_warn(state->ctx, "Background fetch complete!");
+ return;
+ }
+ }
+ DEBUG_MESSAGE((state->ctx, "block requested was %d, fetching %d", state->fill_point, fill));
+ state->fill_point = fill;
+ }
+
+ unlock(state);
+
+ /* Fetch that block */
+ start = fill * BLOCK_SIZE;
+ end = start + BLOCK_SIZE-1;
+ state->current_fill_point = start;
+ if (state->content_length > 0 && start >= state->content_length)
+ state->complete = 1;
+ if (state->content_length > 0 && end >= state->content_length)
+ end = state->content_length-1;
+ snprintf(text, 32, "%d-%d", start, end);
+ curl_easy_setopt(state->handle, CURLOPT_RANGE, text);
+ curl_easy_perform(state->handle);
+}
+
+static void
+fetcher_thread(curl_stream_state *state)
+{
+ while (!state->complete)
+ fetch_chunk(state);
+}
+
+static int
+stream_read(fz_stream *stream, unsigned char *buf, int len)
+{
+ curl_stream_state *state = (curl_stream_state *)stream->state;
+ int len_read = 0;
+ int read_point = stream->pos;
+ int block = read_point>>BLOCK_SHIFT;
+ int left_over = (-read_point) & (BLOCK_SIZE-1);
+
+ if (state->content_length == 0)
+ fz_throw(stream->ctx, FZ_ERROR_TRYLATER, "read of a block we don't have (A) (offset=%d)", read_point);
+
+ if (state->map == NULL)
+ {
+ /* We are doing a simple linear fetch as we don't know the
+ * content length. */
+ if (read_point + len > state->current_fill_point)
+ {
+ stream->rp = stream->wp = stream->bp;
+ fz_throw(stream->ctx, FZ_ERROR_TRYLATER, "read of a block we don't have (B) (offset=%d)", read_point);
+ }
+ memcpy(buf, state->buffer + read_point, len);
+ return len;
+ }
+
+ if (read_point + len > state->content_length)
+ len = state->content_length - read_point;
+ if (left_over > len)
+ left_over = len;
+
+ if (left_over)
+ {
+ /* We are starting midway through a block */
+ if (!HAVE_BLOCK(state->map, block))
+ {
+ lock(state);
+ state->fill_point = block;
+ unlock(state);
+ stream->rp = stream->wp = stream->bp;
+ fz_throw(stream->ctx, FZ_ERROR_TRYLATER, "read of a block we don't have (C) (offset=%d)", read_point);
+ }
+ block++;
+ if (left_over > len)
+ left_over = len;
+ memcpy(buf, state->buffer + read_point, left_over);
+ buf += left_over;
+ read_point += left_over;
+ len -= left_over;
+ len_read += left_over;
+ }
+
+ /* Copy any complete blocks */
+ while (len > BLOCK_SIZE)
+ {
+ if (!HAVE_BLOCK(state->map, block))
+ {
+ lock(state);
+ state->fill_point = block;
+ unlock(state);
+ stream->rp = stream->wp = stream->bp;
+ fz_throw(stream->ctx, FZ_ERROR_TRYLATER, "read of a block we don't have (D) (offset=%d)", read_point);
+ }
+ block++;
+ memcpy(buf, state->buffer + read_point, BLOCK_SIZE);
+ buf += BLOCK_SIZE;
+ read_point += BLOCK_SIZE;
+ len -= BLOCK_SIZE;
+ len_read += BLOCK_SIZE;
+ }
+
+ /* Copy any trailing bytes */
+ if (len > 0)
+ {
+ if (!HAVE_BLOCK(state->map, block))
+ {
+ lock(state);
+ state->fill_point = block;
+ unlock(state);
+ stream->rp = stream->wp = stream->bp;
+ fz_throw(stream->ctx, FZ_ERROR_TRYLATER, "read of a block we don't have (E) (offset=%d)", read_point);
+ }
+ memcpy(buf, state->buffer + read_point, len);
+ len_read += len;
+ }
+
+ return len_read;
+}
+
+static void
+stream_close(fz_context *ctx, void *state_)
+{
+ curl_stream_state *state = (curl_stream_state *)state_;
+
+ if (!state || state->kill_thread)
+ return;
+
+ lock(state);
+ state->kill_thread = 1;
+ unlock(state);
+
+#ifdef _WIN32
+ WaitForSingleObject(state->thread, INFINITE);
+ CloseHandle(state->thread);
+ CloseHandle(state->mutex);
+#else
+ pthread_join(state->thread, NULL);
+ pthread_mutex_destroy(&state->mutex);
+#endif
+
+ fz_free(state->ctx, state->buffer);
+ fz_free(state->ctx, state->map);
+ fz_free(state->ctx, state);
+}
+
+static fz_stream hack_stream;
+static curl_stream_state hack;
+static int hack_pos;
+
+static void
+stream_seek(fz_stream *stream, int offset, int whence)
+{
+ curl_stream_state *state = (curl_stream_state *)stream->state;
+
+ switch(whence)
+ {
+ case SEEK_CUR:
+ offset += stream->pos;
+ break;
+ case SEEK_END:
+ offset += state->content_length;
+ break;
+ default:
+ case SEEK_SET:
+ break;
+ }
+ if (offset < 0)
+ offset = 0;
+ else if (state->content_length > 0 && offset > state->content_length)
+ offset = state->content_length;
+ stream->wp = stream->rp = stream->bp;
+ stream->pos = offset;
+ hack = *state;
+ hack_pos = offset;
+ hack_stream = *stream;
+}
+
+static int
+stream_meta(fz_stream *stream, int key, int size, void *ptr)
+{
+ curl_stream_state *state = (curl_stream_state *)stream->state;
+
+ switch(key)
+ {
+ case FZ_STREAM_META_LENGTH:
+ if (!state->data_arrived)
+ fz_throw(stream->ctx, FZ_ERROR_TRYLATER, "still awaiting file length");
+ return state->content_length;
+ case FZ_STREAM_META_PROGRESSIVE:
+ return 1;
+ }
+ return -1;
+}
+
+fz_stream *fz_stream_from_curl(fz_context *ctx, char *filename, void (*more_data)(void *,int), void *more_data_arg)
+{
+ CURLcode ret;
+ CURL *handle;
+ curl_stream_state *state = fz_malloc_struct(ctx, curl_stream_state);
+ fz_stream *stream;
+
+ ret = curl_global_init(CURL_GLOBAL_ALL);
+ if (ret != 0)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "curl init failed (code %d)", ret);
+
+ state->ctx = ctx;
+ state->handle = handle = curl_easy_init();
+ state->more_data = more_data;
+ state->more_data_arg = more_data_arg;
+
+ curl_easy_setopt(handle, CURLOPT_URL, filename);
+
+ curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 1);
+
+ curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, data_arrived);
+
+ curl_easy_setopt(handle, CURLOPT_WRITEDATA, state);
+
+ curl_easy_setopt(handle, CURLOPT_WRITEHEADER, state);
+
+ curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, header_arrived);
+
+#ifdef _WIN32
+ state->mutex = CreateMutex(NULL, FALSE, NULL);
+ if (state->mutex == NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "mutex creation failed");
+
+ state->thread = CreateThread(NULL, 0, win_thread, state, 0, &state->thread_id);
+ if (state->thread == NULL)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "thread creation failed");
+#else
+ if (pthread_mutex_init(&state->mutex, NULL))
+ fz_throw(ctx, FZ_ERROR_GENERIC, "mutex creation failed");
+
+ if (pthread_create(&state->thread, NULL, pthread_thread, state))
+ fz_throw(ctx, FZ_ERROR_GENERIC, "thread creation failed");
+
+#endif
+
+ stream = fz_new_stream(ctx, state, stream_read, stream_close);
+ stream->seek = stream_seek;
+ stream->meta = stream_meta;
+ return stream;
+}
diff --git a/platform/x11/curl_stream.h b/platform/x11/curl_stream.h
new file mode 100644
index 00000000..8672df17
--- /dev/null
+++ b/platform/x11/curl_stream.h
@@ -0,0 +1,7 @@
+#if !defined(CURL_STREAM_H) && defined(HAVE_CURL)
+
+#define CURL_STREAM_H
+
+fz_stream *fz_stream_from_curl(fz_context *ctx, char *url, void (*more_data)(void *,int), void *more_data_arg);
+
+#endif
diff --git a/platform/x11/jstest_main.c b/platform/x11/jstest_main.c
index a07385bc..4cb11185 100644
--- a/platform/x11/jstest_main.c
+++ b/platform/x11/jstest_main.c
@@ -158,6 +158,10 @@ void winreloadfile(pdfapp_t *app)
pdfapp_open(app, filename, 1);
}
+void winreloadpage(pdfapp_t *app)
+{
+}
+
void winopenuri(pdfapp_t *app, char *buf)
{
}
diff --git a/platform/x11/pdfapp.c b/platform/x11/pdfapp.c
index c891558f..af4cb095 100644
--- a/platform/x11/pdfapp.c
+++ b/platform/x11/pdfapp.c
@@ -1,4 +1,5 @@
#include "pdfapp.h"
+#include "curl_stream.h"
#include <ctype.h> /* for tolower() */
@@ -19,6 +20,12 @@ enum panning
PAN_TO_BOTTOM
};
+enum
+{
+ PDFAPP_OUTLINE_DEFERRED = 1,
+ PDFAPP_OUTLINE_LOAD_NOW = 2
+};
+
static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repaint, int transition);
static void pdfapp_updatepage(pdfapp_t *app);
@@ -160,6 +167,22 @@ void pdfapp_open(pdfapp_t *app, char *filename, int reload)
pdfapp_open_progressive(app, filename, reload, 0);
}
+#ifdef HAVE_CURL
+static void
+pdfapp_more_data(void *app_, int complete)
+{
+ pdfapp_t *app = (pdfapp_t *)app_;
+
+ if (complete && app->outline_deferred == PDFAPP_OUTLINE_DEFERRED)
+ {
+ app->outline_deferred = PDFAPP_OUTLINE_LOAD_NOW;
+ winreloadpage(app);
+ }
+ else if (app->incomplete)
+ winreloadpage(app);
+}
+#endif
+
void pdfapp_open_progressive(pdfapp_t *app, char *filename, int reload, int bps)
{
fz_context *ctx = app->ctx;
@@ -169,6 +192,31 @@ void pdfapp_open_progressive(pdfapp_t *app, char *filename, int reload, int bps)
{
pdf_document *idoc;
+#ifdef HAVE_CURL
+ if (!strncmp(filename, "http://", 7))
+ {
+ app->stream = fz_stream_from_curl(ctx, filename, pdfapp_more_data, app);
+ while (1)
+ {
+ fz_try(ctx)
+ {
+ fz_seek(app->stream, 0, SEEK_SET);
+ app->doc = fz_open_document_with_stream(ctx, filename, app->stream);
+ }
+ fz_catch(ctx)
+ {
+ if (fz_caught(ctx) == FZ_ERROR_TRYLATER)
+ {
+ pdfapp_warn(app, "not enough data to open yet");
+ continue;
+ }
+ fz_rethrow(ctx);
+ }
+ break;
+ }
+ }
+ else
+#endif
if (bps == 0)
{
app->doc = fz_open_document(ctx, filename);
@@ -250,8 +298,7 @@ void pdfapp_open_progressive(pdfapp_t *app, char *filename, int reload, int bps)
{
if (fz_caught(ctx) == FZ_ERROR_TRYLATER)
{
- pdfapp_warn(app, "not enough data to load outline yet - ignoring");
- /* FIXME: Set 'outline_deferred' and retry at end? */
+ app->outline_deferred = PDFAPP_OUTLINE_DEFERRED;
}
else
fz_rethrow(ctx);
@@ -325,6 +372,10 @@ void pdfapp_close(pdfapp_t *app)
fz_close_document(app->doc);
app->doc = NULL;
+#ifdef HAVE_CURL
+ fz_close(app->stream);
+#endif
+
fz_flush_warnings(app->ctx);
}
@@ -486,6 +537,8 @@ static void pdfapp_loadpage(pdfapp_t *app)
app->page_bbox.x1 = 100;
app->page_bbox.y1 = 100;
+ app->incomplete = 0;
+
fz_try(app->ctx)
{
app->page = fz_load_page(app->doc, app->pageno - 1);
@@ -494,7 +547,10 @@ static void pdfapp_loadpage(pdfapp_t *app)
}
fz_catch(app->ctx)
{
- pdfapp_warn(app, "Cannot load page");
+ if (fz_caught(app->ctx) == FZ_ERROR_TRYLATER)
+ app->incomplete = 1;
+ else
+ pdfapp_warn(app, "Cannot load page");
return;
}
@@ -512,14 +568,15 @@ static void pdfapp_loadpage(pdfapp_t *app)
mdev = fz_new_list_device(app->ctx, app->annotations_list);
for (annot = fz_first_annot(app->doc, app->page); annot; annot = fz_next_annot(app->doc, annot))
fz_run_annot(app->doc, app->page, annot, mdev, &fz_identity, &cookie);
- if (cookie.errors)
+ if (cookie.incomplete)
{
- pdfapp_warn(app, "Errors found on page");
- errored = 1;
+ app->incomplete = 1;
+ //pdfapp_warn(app, "Incomplete page rendering");
}
- if (cookie.incomplete)
+ else if (cookie.errors)
{
- pdfapp_warn(app, "Incomplete page rendering");
+ pdfapp_warn(app, "Errors found on page");
+ errored = 1;
}
}
fz_always(app->ctx)
@@ -528,8 +585,13 @@ static void pdfapp_loadpage(pdfapp_t *app)
}
fz_catch(app->ctx)
{
- pdfapp_warn(app, "Cannot load page");
- errored = 1;
+ if (fz_caught(app->ctx) == FZ_ERROR_TRYLATER)
+ app->incomplete = 1;
+ else
+ {
+ pdfapp_warn(app, "Cannot load page");
+ errored = 1;
+ }
}
fz_try(app->ctx)
@@ -538,7 +600,9 @@ static void pdfapp_loadpage(pdfapp_t *app)
}
fz_catch(app->ctx)
{
- if (!errored)
+ if (fz_caught(app->ctx) == FZ_ERROR_TRYLATER)
+ app->incomplete = 1;
+ else if (!errored)
pdfapp_warn(app, "Cannot load page");
}
@@ -564,7 +628,12 @@ static void pdfapp_recreate_annotationslist(pdfapp_t *app)
mdev = fz_new_list_device(app->ctx, app->annotations_list);
for (annot = fz_first_annot(app->doc, app->page); annot; annot = fz_next_annot(app->doc, annot))
fz_run_annot(app->doc, app->page, annot, mdev, &fz_identity, &cookie);
- if (cookie.errors)
+ if (cookie.incomplete)
+ {
+ app->incomplete = 1;
+ //pdfapp_warn(app, "Incomplete page rendering");
+ }
+ else if (cookie.errors)
{
pdfapp_warn(app, "Errors found on page");
errored = 1;
@@ -621,6 +690,23 @@ static void pdfapp_updatepage(pdfapp_t *app)
pdfapp_showpage(app, 0, 0, 1, 0);
}
+void pdfapp_reloadpage(pdfapp_t *app)
+{
+ if (app->outline_deferred == PDFAPP_OUTLINE_LOAD_NOW)
+ {
+ fz_try(app->ctx)
+ {
+ app->outline = fz_load_outline(app->doc);
+ }
+ fz_catch(app->ctx)
+ {
+ /* Ignore any error now */
+ }
+ app->outline_deferred = 0;
+ }
+ pdfapp_showpage(app, 1, 1, 1, 0);
+}
+
static void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repaint, int transition)
{
char buf[MAX_TITLE];
diff --git a/platform/x11/pdfapp.h b/platform/x11/pdfapp.h
index eabb6ca5..9c1dcd0a 100644
--- a/platform/x11/pdfapp.h
+++ b/platform/x11/pdfapp.h
@@ -43,6 +43,7 @@ extern void winprint(pdfapp_t *);
extern void winadvancetimer(pdfapp_t *, float duration);
extern void winreplacefile(char *source, char *target);
extern void wincopyfile(char *source, char *target);
+extern void winreloadpage(pdfapp_t *);
struct pdfapp_s
{
@@ -51,6 +52,7 @@ struct pdfapp_s
char *docpath;
char *doctitle;
fz_outline *outline;
+ int outline_deferred;
int pagecount;
@@ -82,6 +84,7 @@ struct pdfapp_s
fz_text_sheet *page_sheet;
fz_link *page_links;
int errored;
+ int incomplete;
/* snapback history */
int hist[256];
@@ -126,6 +129,9 @@ struct pdfapp_s
void *userdata;
fz_context *ctx;
+#ifdef HAVE_CURL
+ fz_stream *stream;
+#endif
};
void pdfapp_init(fz_context *ctx, pdfapp_t *app);
@@ -142,6 +148,7 @@ void pdfapp_onmouse(pdfapp_t *app, int x, int y, int btn, int modifiers, int sta
void pdfapp_oncopy(pdfapp_t *app, unsigned short *ucsbuf, int ucslen);
void pdfapp_onresize(pdfapp_t *app, int w, int h);
void pdfapp_gotopage(pdfapp_t *app, int number);
+void pdfapp_reloadpage(pdfapp_t *app);
void pdfapp_invert(pdfapp_t *app, const fz_rect *rect);
void pdfapp_inverthit(pdfapp_t *app);
diff --git a/platform/x11/win_main.c b/platform/x11/win_main.c
index 51dada02..6ddd171a 100644
--- a/platform/x11/win_main.c
+++ b/platform/x11/win_main.c
@@ -25,7 +25,7 @@ static HWND hwndview = NULL;
static HDC hdc;
static HBRUSH bgbrush;
static HBRUSH shbrush;
-static BITMAPINFO *dibinf;
+static BITMAPINFO *dibinf = NULL;
static HCURSOR arrowcurs, handcurs, waitcurs, caretcurs;
static LRESULT CALLBACK frameproc(HWND, UINT, WPARAM, LPARAM);
static LRESULT CALLBACK viewproc(HWND, UINT, WPARAM, LPARAM);
@@ -644,11 +644,18 @@ void winopen()
SetCursor(arrowcurs);
}
+static void
+do_close(pdfapp_t *app)
+{
+ pdfapp_close(app);
+ free(dibinf);
+}
+
void winclose(pdfapp_t *app)
{
if (pdfapp_preclose(app))
{
- pdfapp_close(app);
+ do_close(app);
exit(0);
}
}
@@ -877,6 +884,11 @@ void winreloadfile(pdfapp_t *app)
pdfapp_open(app, filename, 1);
}
+void winreloadpage(pdfapp_t *app)
+{
+ SendMessage(hwndview, WM_APP, 0, 0);
+}
+
void winopenuri(pdfapp_t *app, char *buf)
{
ShellExecuteA(hwndframe, "open", buf, 0, 0, SW_SHOWNORMAL);
@@ -1126,6 +1138,12 @@ viewproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
handlemouse(oldx, oldy, 0, 0); /* update cursor */
}
return 0;
+
+ /* We use WM_APP to trigger a reload and repaint of a page */
+ case WM_APP:
+ pdfapp_reloadpage(&gapp);
+ break;
+
}
fflush(stdout);
@@ -1199,7 +1217,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShow
DispatchMessage(&msg);
}
- pdfapp_close(&gapp);
+ do_close(&gapp);
fz_free_context(ctx);
return 0;
diff --git a/platform/x11/x11_main.c b/platform/x11/x11_main.c
index 0bcb98f4..0857046b 100644
--- a/platform/x11/x11_main.c
+++ b/platform/x11/x11_main.c
@@ -70,6 +70,7 @@ static Atom XA_UTF8_STRING;
static Atom WM_DELETE_WINDOW;
static Atom NET_WM_STATE;
static Atom NET_WM_STATE_FULLSCREEN;
+static Atom WM_RELOAD_PAGE;
static int x11fd;
static int xscr;
static Window xwin;
@@ -182,6 +183,7 @@ static void winopen(void)
WM_DELETE_WINDOW = XInternAtom(xdpy, "WM_DELETE_WINDOW", False);
NET_WM_STATE = XInternAtom(xdpy, "_NET_WM_STATE", False);
NET_WM_STATE_FULLSCREEN = XInternAtom(xdpy, "_NET_WM_STATE_FULLSCREEN", False);
+ WM_RELOAD_PAGE = XInternAtom(xdpy, "_WM_RELOAD_PAGE", False);
xscr = DefaultScreen(xdpy);
@@ -652,6 +654,24 @@ void onselreq(Window requestor, Atom selection, Atom target, Atom property, Time
XSendEvent(xdpy, requestor, False, 0, &nevt);
}
+void winreloadpage(pdfapp_t *app)
+{
+ XEvent xev;
+ Display *dpy = XOpenDisplay(NULL);
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.window = xwin;
+ xev.xclient.message_type = WM_RELOAD_PAGE;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = 0;
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = 0;
+ XSendEvent(dpy, xwin, 0, 0, &xev);
+ XCloseDisplay(dpy);
+}
+
void winreloadfile(pdfapp_t *app)
{
pdfapp_close(app);
@@ -886,7 +906,9 @@ int main(int argc, char **argv)
break;
case ClientMessage:
- if (xevt.xclient.format == 32 && xevt.xclient.data.l[0] == WM_DELETE_WINDOW)
+ if (xevt.xclient.message_type == WM_RELOAD_PAGE)
+ pdfapp_reloadpage(&gapp);
+ else if (xevt.xclient.format == 32 && xevt.xclient.data.l[0] == WM_DELETE_WINDOW)
closing = 1;
break;
}