[cairo] How to free a buffer when subset PS Type1 generation is failed?
mpsuzuki at hiroshima-u.ac.jp
mpsuzuki at hiroshima-u.ac.jp
Thu Jan 7 20:31:59 PST 2010
Hi,
Now I'm playing with pdftoppm (poppler utils) to attach
a shell-like interface, to cropping many fragments from
a signe huge PDF. During the development, I found a
problem that cairo_type1_font_subset_generate() does not
free a buffer allocated by _cairo_output_stream_create()
in some case, and it behaves like a memory leak during
PDF generation. I want to ask the appropriate method to
free it.
In following, I explain my understanding of current implementation.
cairo/src/cairo-type1-subset.c
1214 static cairo_status_t
1215 cairo_type1_font_subset_generate (void *abstract_font,
1216 const char *name)
1217
1218 {
1219 cairo_type1_font_subset_t *font = abstract_font;
1220 cairo_ft_unscaled_font_t *ft_unscaled_font;
1221 unsigned long ret;
1222 cairo_status_t status;
[snip]
1258 font->output = _cairo_output_stream_create (type1_font_write, NULL, font);
Here, _cairo_output_stream_create() is called, and a buffer
is allocated by malloc() to hold the function pointers,
type1_font_write, NULL (=type1_font_close), and font.
1259 if (_cairo_output_stream_get_status (font->output)) {
1260 status = _cairo_output_stream_destroy (font->output);
1261 goto fail;
1262 }
Here, the status of the stream is found to be bad, so
it's destroyed. _cairo_output_stream_destroy() invokes
free() and the allocated buffer is freed.
1263
1264 status = cairo_type1_font_subset_write (font, name);
1265 if (unlikely (status))
1266 goto fail;
Here, if we got any error in the writing subsetted PS
Type1 data to the stream, we jump to fail without
freeing the allocated buffer.
1267
1268 font->base.data = _cairo_array_index (&font->contents, 0);
Here, if we successfully finished to write subsetted
PS Type1 data to the stream, the base font information
is updated to refer the generated font in the stream.
The stream is referred, so it should not be freed.
1269
1270 fail:
1271 _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font);
1272
1273 return status;
1274 }
1275
Unlock the face and return the caller.
The problem I found is that if we got any error in the
writing of subssetted PS Type1 font to the stream
(in my case, the passed font is recognized as unsupported
format), the stream would not be referred anymore, but
the buffer holding the stream is not freed.
If I insert _cairo_output_stream_destroy() for failure
case, aslike
1264 status = cairo_type1_font_subset_write (font, name);
1265 if (unlikely (status))
1266 _cairo_output_stream_destroy (font->output);
1267 else
1268 font->base.data = _cairo_array_index (&font->contents, 0);
the buffer is freed. Is this appropriate fix?
--
Also I have to note the reason why I don't check the
return value of _cairo_output_stream_destroy().
The return value of _cairo_output_stream_destroy() notices
the success/failure of the process to destroy the stream.
The most important informations to be returned to the caller
of cairo_type1_font_subset_generate() is the status of the
subsetted PS Type1 font generation. When the font generation
itself is failed, this failure information is more important
than the status of stream destroy process. So I didn't
overwrite the `status' by _cairo_output_stream_destroy().
The overwriting of the status by stream destroyer, aslike
1264 status = cairo_type1_font_subset_write (font, name);
1265 if (unlikely (status))
1266 status = _cairo_output_stream_destroy (font->output);
1267 else
1268 font->base.data = _cairo_array_index (&font->contents, 0);
is not good idea, I think. Because it behaves aslike the
font generation finishes successfully when the font generation
is failed but the stream is destroyed successfully.
If I have any fallback for the failure of the stream
destroyer (if there's such please let me know), I have to
check the return value.
Because of similar reason, I'm questionable if the stream
destroyer should overwrite the status, like here:
1259 if (_cairo_output_stream_get_status (font->output)) {
1260 status = _cairo_output_stream_destroy (font->output);
1261 goto fail;
1262 }
--
Following is a patch summarizing my idea in above.
Please give me comment.
Regards,
mpsuzuki
diff --git a/src/cairo-type1-subset.c b/src/cairo-type1-subset.c
index 304353a..b5b3002 100644
--- a/src/cairo-type1-subset.c
+++ b/src/cairo-type1-subset.c
@@ -1256,16 +1256,17 @@ cairo_type1_font_subset_generate (void *abstract_font,
goto fail;
font->output = _cairo_output_stream_create (type1_font_write, NULL, font);
- if (_cairo_output_stream_get_status (font->output)) {
- status = _cairo_output_stream_destroy (font->output);
+ if (unlikely (status)) {
+ _cairo_output_stream_destroy (font->output);
+ status = CAIRO_STATUS_NO_MEMORY;
goto fail;
}
status = cairo_type1_font_subset_write (font, name);
if (unlikely (status))
- goto fail;
-
- font->base.data = _cairo_array_index (&font->contents, 0);
+ _cairo_output_stream_destroy (font->output);
+ else
+ font->base.data = _cairo_array_index (&font->contents, 0);
fail:
_cairo_ft_unscaled_font_unlock_face (ft_unscaled_font);
More information about the cairo
mailing list