mirror of
				https://github.com/michaelrsweet/pdfio.git
				synced 2025-10-31 10:26:22 +01:00 
			
		
		
		
	Initial writing support.
This commit is contained in:
		| @@ -573,7 +573,7 @@ _pdfioArrayWrite(pdfio_array_t *a)	// I - Array | ||||
|   // Write each value... | ||||
|   for (i = a->num_values, v = a->values; i > 0; i --, v ++) | ||||
|   { | ||||
|     if (!_pdfioValueWrite(pdf, v)) | ||||
|     if (!_pdfioValueWrite(pdf, v, NULL)) | ||||
|       return (false); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -805,7 +805,7 @@ _pdfioDictWrite(pdfio_dict_t *dict,	// I - Dictionary | ||||
|       if (!_pdfioFilePuts(pdf, " 9999999999")) | ||||
|         return (false); | ||||
|     } | ||||
|     else if (!_pdfioValueWrite(pdf, &pair->value)) | ||||
|     else if (!_pdfioValueWrite(pdf, &pair->value, NULL)) | ||||
|       return (false); | ||||
|   } | ||||
|  | ||||
|   | ||||
							
								
								
									
										251
									
								
								pdfio-file.c
									
									
									
									
									
								
							
							
						
						
									
										251
									
								
								pdfio-file.c
									
									
									
									
									
								
							| @@ -27,6 +27,8 @@ static int		compare_objs(pdfio_obj_t **a, pdfio_obj_t **b); | ||||
| static bool		load_obj_stream(pdfio_obj_t *obj); | ||||
| static bool		load_pages(pdfio_file_t *pdf, pdfio_obj_t *obj); | ||||
| static bool		load_xref(pdfio_file_t *pdf, off_t xref_offset); | ||||
| static bool		write_catalog(pdfio_file_t *pdf); | ||||
| static bool		write_pages(pdfio_file_t *pdf); | ||||
| static bool		write_trailer(pdfio_file_t *pdf); | ||||
|  | ||||
|  | ||||
| @@ -89,7 +91,14 @@ pdfioFileClose(pdfio_file_t *pdf)	// I - PDF file | ||||
|  | ||||
|   // Close the file itself... | ||||
|   if (pdf->mode == _PDFIO_MODE_WRITE) | ||||
|     ret = write_trailer(pdf); | ||||
|   { | ||||
|     ret = false; | ||||
|  | ||||
|     if (write_pages(pdf)) | ||||
|       if (write_catalog(pdf)) | ||||
|         if (write_trailer(pdf)) | ||||
|           ret = _pdfioFileFlush(pdf); | ||||
|   } | ||||
|  | ||||
|   if (close(pdf->fd) < 0) | ||||
|     ret = false; | ||||
| @@ -132,10 +141,13 @@ pdfio_file_t *				// O - PDF file or `NULL` on error | ||||
| pdfioFileCreate( | ||||
|     const char       *filename,		// I - Filename | ||||
|     const char       *version,		// I - PDF version number or `NULL` for default (2.0) | ||||
|     pdfio_rect_t     *media_box,	// I - Default MediaBox for pages | ||||
|     pdfio_rect_t     *crop_box,		// I - Default CropBox for pages | ||||
|     pdfio_error_cb_t error_cb,		// I - Error callback or `NULL` for default | ||||
|     void             *error_data)	// I - Error callback data, if any | ||||
| { | ||||
|   pdfio_file_t	*pdf;			// PDF file | ||||
|   pdfio_dict_t	*dict;			// Dictionary for pages object | ||||
|  | ||||
|  | ||||
|   // Range check input... | ||||
| @@ -169,6 +181,30 @@ pdfioFileCreate( | ||||
|   pdf->error_cb   = error_cb; | ||||
|   pdf->error_data = error_data; | ||||
|  | ||||
|   if (media_box) | ||||
|   { | ||||
|     pdf->media_box = *media_box; | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     // Default to "universal" size (intersection of A4 and US Letter) | ||||
|     pdf->media_box.x2 = 210.0f * 72.0f / 25.4f; | ||||
|     pdf->media_box.y2 = 11.0f * 72.0f; | ||||
|   } | ||||
|  | ||||
|   if (crop_box) | ||||
|   { | ||||
|     pdf->crop_box = *crop_box; | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     // Default to "universal" size (intersection of A4 and US Letter) with 1/4" margins | ||||
|     pdf->crop_box.x1 = 18.0f; | ||||
|     pdf->crop_box.y1 = 18.0f; | ||||
|     pdf->crop_box.x2 = 210.0f * 72.0f / 25.4f - 18.0f; | ||||
|     pdf->crop_box.y2 = 11.0f * 72.0f - 18.0f; | ||||
|   } | ||||
|  | ||||
|   // Create the file... | ||||
|   if ((pdf->fd = open(filename, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0666)) < 0) | ||||
|   { | ||||
| @@ -187,6 +223,23 @@ pdfioFileCreate( | ||||
|     return (NULL); | ||||
|   } | ||||
|  | ||||
|   // Create the pages object... | ||||
|   if ((dict = pdfioDictCreate(pdf)) == NULL) | ||||
|   { | ||||
|     pdfioFileClose(pdf); | ||||
|     unlink(filename); | ||||
|     return (NULL); | ||||
|   } | ||||
|  | ||||
|   pdfioDictSetName(dict, "Type", "Pages"); | ||||
|  | ||||
|   if ((pdf->pages_root = pdfioFileCreateObject(pdf, dict)) == NULL) | ||||
|   { | ||||
|     pdfioFileClose(pdf); | ||||
|     unlink(filename); | ||||
|     return (NULL); | ||||
|   } | ||||
|  | ||||
|   return (pdf); | ||||
| } | ||||
|  | ||||
| @@ -246,7 +299,6 @@ pdfioFileCreateObject( | ||||
|   // Initialize the object... | ||||
|   obj->pdf              = pdf; | ||||
|   obj->number           = pdf->num_objs; | ||||
|   obj->offset           = _pdfioFileTell(pdf); | ||||
|   obj->value.type       = PDFIO_VALTYPE_DICT; | ||||
|   obj->value.value.dict = dict; | ||||
|  | ||||
| @@ -259,14 +311,73 @@ pdfioFileCreateObject( | ||||
| // 'pdfioFileCreatePage()' - Create a page in a PDF file. | ||||
| // | ||||
|  | ||||
| pdfio_obj_t *				// O - New object | ||||
| pdfio_stream_t *			// O - Contents stream | ||||
| pdfioFileCreatePage(pdfio_file_t *pdf,	// I - PDF file | ||||
|                     pdfio_dict_t *dict)	// I - Page dictionary | ||||
| { | ||||
|   // TODO: Implement pdfioFileCreatePage | ||||
|   (void)pdf; | ||||
|   (void)dict; | ||||
|   pdfio_obj_t	*page,			// Page object | ||||
| 		*contents;		// Contents object | ||||
|   pdfio_dict_t	*contents_dict;		// Dictionary for Contents object | ||||
|  | ||||
|  | ||||
|   // Range check input... | ||||
|   if (!pdf) | ||||
|     return (NULL); | ||||
|  | ||||
|   // Copy the page dictionary... | ||||
|   if (dict) | ||||
|     dict = pdfioDictCopy(pdf, dict); | ||||
|   else | ||||
|     dict = pdfioDictCreate(pdf); | ||||
|  | ||||
|   // Make sure the page dictionary has all of the required keys... | ||||
|   if (!_pdfioDictGetValue(dict, "CropBox")) | ||||
|     pdfioDictSetRect(dict, "CropBox", &pdf->crop_box); | ||||
|  | ||||
|   if (!_pdfioDictGetValue(dict, "MediaBox")) | ||||
|     pdfioDictSetRect(dict, "MediaBox", &pdf->media_box); | ||||
|  | ||||
|   pdfioDictSetObject(dict, "Parent", pdf->pages_root); | ||||
|  | ||||
|   if (!_pdfioDictGetValue(dict, "Resources")) | ||||
|     pdfioDictSetDict(dict, "Resources", pdfioDictCreate(pdf)); | ||||
|  | ||||
|   if (!_pdfioDictGetValue(dict, "Type")) | ||||
|     pdfioDictSetName(dict, "Type", "Page"); | ||||
|  | ||||
|   // Create the page object... | ||||
|   page = pdfioFileCreateObject(pdf, dict); | ||||
|  | ||||
|   // Create a contents object to hold the contents of the page... | ||||
|   contents_dict = pdfioDictCreate(pdf); | ||||
|   pdfioDictSetName(contents_dict, "Filter", "FlateDecode"); | ||||
|  | ||||
|   contents = pdfioFileCreateObject(pdf, contents_dict); | ||||
|  | ||||
|   // Add the contents stream to the pages object and write it... | ||||
|   pdfioDictSetObject(dict, "Contents", contents); | ||||
|   if (!pdfioObjClose(page)) | ||||
|     return (NULL); | ||||
|  | ||||
|   // Add the page to the array of pages... | ||||
|   if (pdf->num_pages >= pdf->alloc_pages) | ||||
|   { | ||||
|     pdfio_obj_t **temp = (pdfio_obj_t **)realloc(pdf->pages, (pdf->alloc_pages + 16) * sizeof(pdfio_obj_t *)); | ||||
|  | ||||
|     if (!temp) | ||||
|     { | ||||
|       _pdfioFileError(pdf, "Unable to allocate memory for pages."); | ||||
|       return (NULL); | ||||
|     } | ||||
|  | ||||
|     pdf->alloc_pages += 16; | ||||
|     pdf->pages       = temp; | ||||
|   } | ||||
|  | ||||
|   pdf->pages[pdf->num_pages ++] = page; | ||||
|  | ||||
|   // Create the contents stream... | ||||
|   return (pdfioObjCreateStream(contents, PDFIO_FILTER_FLATE)); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -1114,6 +1225,56 @@ load_xref(pdfio_file_t *pdf,		// I - PDF file | ||||
| } | ||||
|  | ||||
|  | ||||
| // | ||||
| // 'write_catalog()' - Write the PDF root object/catalog. | ||||
| // | ||||
|  | ||||
| static bool				// O - `true` on success, `false` on failure | ||||
| write_catalog(pdfio_file_t *pdf)	// I - PDF file | ||||
| { | ||||
|   pdfio_dict_t	*dict;			// Dictionary for catalog... | ||||
|  | ||||
|  | ||||
|   if ((dict = pdfioDictCreate(pdf)) == NULL) | ||||
|     return (false); | ||||
|  | ||||
|   pdfioDictSetName(dict, "Type", "Catalog"); | ||||
|   pdfioDictSetObject(dict, "Pages", pdf->pages_root); | ||||
|   // TODO: Add support for all of the root object dictionary keys | ||||
|  | ||||
|   if ((pdf->root = pdfioFileCreateObject(pdf, dict)) == NULL) | ||||
|     return (false); | ||||
|   else | ||||
|     return (pdfioObjClose(pdf->root)); | ||||
| } | ||||
|  | ||||
|  | ||||
| // | ||||
| // 'write_pages()' - Write the PDF pages objects. | ||||
| // | ||||
|  | ||||
| static bool				// O - `true` on success, `false` on failure | ||||
| write_pages(pdfio_file_t *pdf)		// I - PDF file | ||||
| { | ||||
|   pdfio_array_t	*kids;			// Pages array | ||||
|   size_t	i;			// Looping var | ||||
|  | ||||
|  | ||||
|   // Build the "Kids" array pointing to each page... | ||||
|   if ((kids = pdfioArrayCreate(pdf)) == NULL) | ||||
|     return (false); | ||||
|  | ||||
|   for (i = 0; i < pdf->num_pages; i ++) | ||||
|     pdfioArrayAppendObject(kids, pdf->pages[i]); | ||||
|  | ||||
|   pdfioDictSetNumber(pdf->pages_root->value.value.dict, "Count", pdf->num_pages); | ||||
|   pdfioDictSetArray(pdf->pages_root->value.value.dict, "Kids", kids); | ||||
|  | ||||
|   // Write the Pages object... | ||||
|   return (pdfioObjClose(pdf->pages_root)); | ||||
| } | ||||
|  | ||||
|  | ||||
| // | ||||
| // 'write_trailer()' - Write the PDF catalog object, xref table, and trailer. | ||||
| // | ||||
| @@ -1121,8 +1282,80 @@ load_xref(pdfio_file_t *pdf,		// I - PDF file | ||||
| static bool				// O - `true` on success, `false` on failure | ||||
| write_trailer(pdfio_file_t *pdf)	// I - PDF file | ||||
| { | ||||
|   // TODO: Write trailer | ||||
|   (void)pdf; | ||||
|   bool		ret = true;		// Return value | ||||
|   off_t		xref_offset;		// Offset to xref table | ||||
|   size_t	i;			// Looping var | ||||
|   int		fd;			// File for /dev/urandom | ||||
|   unsigned char	id_values[2][16];	// ID array values | ||||
|  | ||||
|   return (false); | ||||
|  | ||||
|   // Write the xref table... | ||||
|   // TODO: Look at adding support for xref streams... | ||||
|   xref_offset = _pdfioFileTell(pdf); | ||||
|  | ||||
|   if (!_pdfioFilePrintf(pdf, "xref\n0 %lu \n0000000000 65535 f \n", (unsigned long)pdf->num_objs)) | ||||
|   { | ||||
|     _pdfioFileError(pdf, "Unable to write cross-reference table."); | ||||
|     ret = false; | ||||
|     goto done; | ||||
|   } | ||||
|  | ||||
|   for (i = 0; i < pdf->num_objs; i ++) | ||||
|   { | ||||
|     pdfio_obj_t	*obj = pdf->objs[i];	// Current object | ||||
|  | ||||
|     if (!_pdfioFilePrintf(pdf, "%010lu %05u n \n", (unsigned long)obj->offset, obj->generation)) | ||||
|     { | ||||
|       _pdfioFileError(pdf, "Unable to write cross-reference table."); | ||||
|       ret = false; | ||||
|       goto done; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Write the trailer... | ||||
|   if (!_pdfioFilePuts(pdf, "trailer\n")) | ||||
|   { | ||||
|     _pdfioFileError(pdf, "Unable to write trailer."); | ||||
|     ret = false; | ||||
|     goto done; | ||||
|   } | ||||
|  | ||||
|   if ((fd = open("/dev/urandom", O_RDONLY)) >= 0) | ||||
|   { | ||||
|     // Load ID array with random values from /dev/urandom... | ||||
|     memset(id_values, 0, sizeof(id_values)); | ||||
|     read(fd, id_values[0], sizeof(id_values[0])); | ||||
|     read(fd, id_values[1], sizeof(id_values[1])); | ||||
|     close(fd); | ||||
|  | ||||
|     pdf->id_array = pdfioArrayCreate(pdf); | ||||
|     pdfioArrayAppendBinary(pdf->id_array, id_values[0], sizeof(id_values[0])); | ||||
|     pdfioArrayAppendBinary(pdf->id_array, id_values[1], sizeof(id_values[1])); | ||||
|   } | ||||
|  | ||||
|   pdf->trailer = pdfioDictCreate(pdf); | ||||
|   if (pdf->encrypt) | ||||
|     pdfioDictSetObject(pdf->trailer, "Encrypt", pdf->encrypt); | ||||
|   if (pdf->id_array) | ||||
|     pdfioDictSetArray(pdf->trailer, "ID", pdf->id_array); | ||||
|   pdfioDictSetObject(pdf->trailer, "Info", pdf->info); | ||||
|   pdfioDictSetObject(pdf->trailer, "Root", pdf->root); | ||||
|   pdfioDictSetNumber(pdf->trailer, "Size", pdf->num_objs + 1); | ||||
|  | ||||
|   if (!_pdfioDictWrite(pdf->trailer, NULL)) | ||||
|   { | ||||
|     _pdfioFileError(pdf, "Unable to write trailer."); | ||||
|     ret = false; | ||||
|     goto done; | ||||
|   } | ||||
|  | ||||
|   if (!_pdfioFilePrintf(pdf, "\nstartxref\n%lu\n%%EOF\n", (unsigned long)xref_offset)) | ||||
|   { | ||||
|     _pdfioFileError(pdf, "Unable to write xref offset."); | ||||
|     ret = false; | ||||
|   } | ||||
|  | ||||
|   done: | ||||
|  | ||||
|   return (ret); | ||||
| } | ||||
|   | ||||
| @@ -14,6 +14,13 @@ | ||||
| #include "pdfio-private.h" | ||||
|  | ||||
|  | ||||
| // | ||||
| // Local functions... | ||||
| // | ||||
|  | ||||
| static bool	write_obj_header(pdfio_obj_t *obj); | ||||
|  | ||||
|  | ||||
| // | ||||
| // 'pdfioObjClose()' - Close an object, writing any data as needed to the PDF | ||||
| //                     file. | ||||
| @@ -22,10 +29,20 @@ | ||||
| bool					// O - `true` on success, `false` on failure | ||||
| pdfioObjClose(pdfio_obj_t *obj)		// I - Object | ||||
| { | ||||
|   // TODO: Implement pdfioObjClose | ||||
|   (void)obj; | ||||
|  | ||||
|   // Range check input | ||||
|   if (!obj) | ||||
|     return (false); | ||||
|  | ||||
|   if (obj->pdf->mode != _PDFIO_MODE_WRITE) | ||||
|     return (true);			// Nothing to do when reading | ||||
|  | ||||
|   // Write what remains for the object... | ||||
|   if (!obj->offset) | ||||
|     return (write_obj_header(obj));	// Just write the object value | ||||
|   else if (obj->stream) | ||||
|     return (pdfioStreamClose(obj->stream)); | ||||
|   else | ||||
|     return (true);			// Already closed | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -54,11 +71,40 @@ pdfioObjCreateStream( | ||||
|     pdfio_obj_t    *obj,		// I - Object | ||||
|     pdfio_filter_t filter)		// I - Type of compression to apply | ||||
| { | ||||
|   // TODO: Implement pdfioObjCreateStream | ||||
|   (void)obj; | ||||
|   (void)filter; | ||||
|  | ||||
|   // Range check input | ||||
|   if (!obj || obj->pdf->mode != _PDFIO_MODE_WRITE || obj->value.type != PDFIO_VALTYPE_DICT) | ||||
|     return (NULL); | ||||
|  | ||||
|   if (obj->offset) | ||||
|   { | ||||
|     _pdfioFileError(obj->pdf, "Object has already been written."); | ||||
|     return (NULL); | ||||
|   } | ||||
|  | ||||
|   if (filter != PDFIO_FILTER_NONE && filter != PDFIO_FILTER_FLATE) | ||||
|   { | ||||
|     _pdfioFileError(obj->pdf, "Unsupported filter value for pdfioObjCreateStream."); | ||||
|     return (NULL); | ||||
|   } | ||||
|  | ||||
|   // Write the header... | ||||
|   if (!_pdfioDictGetValue(obj->value.value.dict, "Length")) | ||||
|   { | ||||
|     // Need a Length key for the stream, add a placeholder that we can fill in | ||||
|     // later... | ||||
|     pdfioDictSetNumber(obj->value.value.dict, "Length", 0.0f); | ||||
|   } | ||||
|  | ||||
|   if (!write_obj_header(obj)) | ||||
|     return (NULL); | ||||
|  | ||||
|   if (!_pdfioFilePuts(obj->pdf, "stream\n")) | ||||
|     return (NULL); | ||||
|  | ||||
|   obj->stream_offset = _pdfioFileTell(obj->pdf); | ||||
|  | ||||
|   // Return the new stream... | ||||
|   return (_pdfioStreamCreate(obj, filter)); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -295,3 +341,22 @@ pdfioObjOpenStream(pdfio_obj_t *obj,	// I - Object | ||||
|   // Open the stream... | ||||
|   return (_pdfioStreamOpen(obj, decode)); | ||||
| } | ||||
|  | ||||
|  | ||||
| // | ||||
| // 'write_obj_header()' - Write the object header... | ||||
| // | ||||
|  | ||||
| static bool				// O - `true` on success, `false` on failure | ||||
| write_obj_header(pdfio_obj_t *obj)	// I - Object | ||||
| { | ||||
|   obj->offset = _pdfioFileTell(obj->pdf); | ||||
|  | ||||
|   if (!_pdfioFilePrintf(obj->pdf, "%lu %u obj\n", (unsigned long)obj->number, obj->generation)) | ||||
|     return (false); | ||||
|  | ||||
|   if (!_pdfioValueWrite(obj->pdf, &obj->value, &obj->length_offset)) | ||||
|     return (false); | ||||
|  | ||||
|   return (_pdfioFilePuts(obj->pdf, "\n")); | ||||
| } | ||||
|   | ||||
| @@ -77,7 +77,8 @@ typedef enum _pdfio_predictor_e		// PNG predictor constants | ||||
|   _PDFIO_PREDICTOR_PNG_SUB = 11,	// PNG Sub predictor | ||||
|   _PDFIO_PREDICTOR_PNG_UP = 12,		// PNG Up predictor | ||||
|   _PDFIO_PREDICTOR_PNG_AVERAGE = 13,	// PNG Average predictor | ||||
|   _PDFIO_PREDICTOR_PNG_PAETH = 14	// PNG Paeth predictor | ||||
|   _PDFIO_PREDICTOR_PNG_PAETH = 14,	// PNG Paeth predictor | ||||
|   _PDFIO_PREDICTOR_PNG_AUTO = 15	// PNG "auto" predictor (currently mapped to Paeth) | ||||
| } _pdfio_predictor_t; | ||||
|  | ||||
| typedef ssize_t (*_pdfio_tconsume_cb_t)(void *data, size_t bytes); | ||||
| @@ -154,6 +155,8 @@ struct _pdfio_file_s			// PDF file structure | ||||
| { | ||||
|   char		*filename;		// Filename | ||||
|   char		*version;		// Version number | ||||
|   pdfio_rect_t	media_box,		// Default MediaBox value | ||||
| 		crop_box;		// Default CropBox value | ||||
|   _pdfio_mode_t	mode;			// Read/write mode | ||||
|   pdfio_error_cb_t error_cb;		// Error callback | ||||
|   void		*error_data;		// Data for error callback | ||||
| @@ -167,6 +170,7 @@ struct _pdfio_file_s			// PDF file structure | ||||
|   pdfio_dict_t	*trailer;		// Trailer dictionary | ||||
|   pdfio_obj_t	*root;			// Root object/dictionary | ||||
|   pdfio_obj_t	*info;			// Information object/dictionary | ||||
|   pdfio_obj_t	*pages_root;		// Root pages object | ||||
|   pdfio_obj_t	*encrypt;		// Encryption object/dictionary | ||||
|   pdfio_array_t	*id_array;		// ID array | ||||
|  | ||||
| @@ -275,6 +279,6 @@ extern _pdfio_value_t	*_pdfioValueCopy(pdfio_file_t *pdfdst, _pdfio_value_t *vds | ||||
| extern void		_pdfioValueDebug(_pdfio_value_t *v, FILE *fp) PDFIO_INTERNAL; | ||||
| extern void		_pdfioValueDelete(_pdfio_value_t *v) PDFIO_INTERNAL; | ||||
| extern _pdfio_value_t	*_pdfioValueRead(pdfio_file_t *pdf, _pdfio_token_t *ts, _pdfio_value_t *v) PDFIO_INTERNAL; | ||||
| extern bool		_pdfioValueWrite(pdfio_file_t *pdf, _pdfio_value_t *v) PDFIO_INTERNAL; | ||||
| extern bool		_pdfioValueWrite(pdfio_file_t *pdf, _pdfio_value_t *v, off_t *length) PDFIO_INTERNAL; | ||||
|  | ||||
| #endif // !PDFIO_PRIVATE_H | ||||
|   | ||||
							
								
								
									
										331
									
								
								pdfio-stream.c
									
									
									
									
									
								
							
							
						
						
									
										331
									
								
								pdfio-stream.c
									
									
									
									
									
								
							| @@ -20,6 +20,7 @@ | ||||
|  | ||||
| static unsigned char	stream_paeth(unsigned char a, unsigned char b, unsigned char c); | ||||
| static ssize_t		stream_read(pdfio_stream_t *st, char *buffer, size_t bytes); | ||||
| static bool		stream_write(pdfio_stream_t *st, const void *buffer, size_t bytes); | ||||
|  | ||||
|  | ||||
| // | ||||
| @@ -29,6 +30,9 @@ static ssize_t		stream_read(pdfio_stream_t *st, char *buffer, size_t bytes); | ||||
| bool					// O - `true` on success, `false` on failure | ||||
| pdfioStreamClose(pdfio_stream_t *st)	// I - Stream | ||||
| { | ||||
|   bool ret = true;			// Return value | ||||
|  | ||||
|  | ||||
|   // Range check input... | ||||
|   if (!st) | ||||
|     return (false); | ||||
| @@ -41,15 +45,85 @@ pdfioStreamClose(pdfio_stream_t *st)	// I - Stream | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     // TODO: Implement close for writing | ||||
|     return (false); | ||||
|     // Close stream for writing... | ||||
|     if (st->filter == PDFIO_FILTER_FLATE) | ||||
|     { | ||||
|       // Finalize flate compression stream... | ||||
|       int status;			// Deflate status | ||||
|  | ||||
|       while ((status = deflate(&st->flate, Z_FINISH)) != Z_STREAM_END) | ||||
|       { | ||||
| 	if (status < Z_OK && status != Z_BUF_ERROR) | ||||
| 	{ | ||||
| 	  _pdfioFileError(st->pdf, "Flate compression failed (%d)", status); | ||||
| 	  ret = false; | ||||
| 	  goto done; | ||||
| 	} | ||||
|  | ||||
| 	if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out)) | ||||
| 	{ | ||||
| 	  ret = false; | ||||
| 	  goto done; | ||||
| 	} | ||||
|  | ||||
| 	st->flate.next_out  = (Bytef *)st->cbuffer; | ||||
| 	st->flate.avail_out = (uInt)sizeof(st->cbuffer); | ||||
|       } | ||||
|  | ||||
|       if (st->flate.avail_out < (uInt)sizeof(st->cbuffer)) | ||||
|       { | ||||
|         // Write any residuals... | ||||
| 	if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out)) | ||||
| 	{ | ||||
| 	  ret = false; | ||||
| 	  goto done; | ||||
| 	} | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Save the length of this stream... | ||||
|     st->obj->stream_length = (size_t)(_pdfioFileTell(st->pdf) - st->obj->stream_offset); | ||||
|  | ||||
|     // End of stream marker... | ||||
|     if (!_pdfioFilePuts(st->pdf, "\nendstream\n")) | ||||
|     { | ||||
|       ret = false; | ||||
|       goto done; | ||||
|     } | ||||
|  | ||||
|     // Update the length as needed... | ||||
|     if (st->obj->length_offset) | ||||
|     { | ||||
|       // Seek back to the "/Length 9999999999" we wrote... | ||||
|       if (_pdfioFileSeek(st->pdf, st->obj->length_offset, SEEK_SET) < 0) | ||||
|       { | ||||
|         ret = false; | ||||
|         goto done; | ||||
|       } | ||||
|  | ||||
|       // Write the updated length value... | ||||
|       if (!_pdfioFilePrintf(st->pdf, "%-10lu", (unsigned long)st->obj->stream_length)) | ||||
|       { | ||||
|         ret = false; | ||||
|         goto done; | ||||
|       } | ||||
|  | ||||
|       // Seek to the end of the PDF file... | ||||
|       if (_pdfioFileSeek(st->pdf, 0, SEEK_END) < 0) | ||||
|       { | ||||
|         ret = false; | ||||
|         goto done; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   done: | ||||
|  | ||||
|   free(st->pbuffers[0]); | ||||
|   free(st->pbuffers[1]); | ||||
|   free(st); | ||||
|  | ||||
|   return (true); | ||||
|   return (ret); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -64,13 +138,112 @@ _pdfioStreamCreate( | ||||
|     pdfio_obj_t    *obj,		// I - Object | ||||
|     pdfio_filter_t compression)		// I - Compression to apply | ||||
| { | ||||
|   // TODO: Implement _pdfioStreamCreate | ||||
|   (void)obj; | ||||
|   (void)compression; | ||||
|   pdfio_stream_t	*st;		// Stream | ||||
|  | ||||
|  | ||||
|   // Allocate a new stream object... | ||||
|   if ((st = (pdfio_stream_t *)calloc(1, sizeof(pdfio_stream_t))) == NULL) | ||||
|   { | ||||
|     _pdfioFileError(obj->pdf, "Unable to allocate memory for a stream."); | ||||
|     return (NULL); | ||||
|   } | ||||
|  | ||||
|   st->pdf    = obj->pdf; | ||||
|   st->obj    = obj; | ||||
|   st->filter = compression; | ||||
|  | ||||
|   if (compression == PDFIO_FILTER_FLATE) | ||||
|   { | ||||
|     // Flate compression | ||||
|     pdfio_dict_t *params = pdfioDictGetDict(obj->value.value.dict, "DecodeParms"); | ||||
| 					// Decoding parameters | ||||
|     int bpc = (int)pdfioDictGetNumber(params, "BitsPerComponent"); | ||||
| 					// Bits per component | ||||
|     int colors = (int)pdfioDictGetNumber(params, "Colors"); | ||||
| 					// Number of colors | ||||
|     int columns = (int)pdfioDictGetNumber(params, "Columns"); | ||||
| 					// Number of columns | ||||
|     int predictor = (int)pdfioDictGetNumber(params, "Predictor"); | ||||
| 					// Predictory value, if any | ||||
|  | ||||
|     PDFIO_DEBUG("_pdfioStreamCreate: FlateDecode - BitsPerComponent=%d, Colors=%d, Columns=%d, Predictor=%d\n", bpc, colors, columns, predictor); | ||||
|  | ||||
|     if (bpc == 0) | ||||
|     { | ||||
|       bpc = 8; | ||||
|     } | ||||
|     else if (bpc < 1 || bpc == 3 || (bpc > 4 && bpc < 8) || (bpc > 8 && bpc < 16) || bpc > 16) | ||||
|     { | ||||
|       _pdfioFileError(st->pdf, "Unsupported BitsPerColor value %d.", bpc); | ||||
|       free(st); | ||||
|       return (NULL); | ||||
|     } | ||||
|  | ||||
|     if (colors == 0) | ||||
|     { | ||||
|       colors = 1; | ||||
|     } | ||||
|     else if (colors < 0 || colors > 4) | ||||
|     { | ||||
|       _pdfioFileError(st->pdf, "Unsupported Colors value %d.", colors); | ||||
|       free(st); | ||||
|       return (NULL); | ||||
|     } | ||||
|  | ||||
|     if (columns == 0) | ||||
|     { | ||||
|       columns = 1; | ||||
|     } | ||||
|     else if (columns < 0) | ||||
|     { | ||||
|       _pdfioFileError(st->pdf, "Unsupported Columns value %d.", columns); | ||||
|       free(st); | ||||
|       return (NULL); | ||||
|     } | ||||
|  | ||||
|     if ((predictor > 1 && predictor < 10) || predictor > 15) | ||||
|     { | ||||
|       _pdfioFileError(st->pdf, "Unsupported Predictor function %d.", predictor); | ||||
|       free(st); | ||||
|       return (NULL); | ||||
|     } | ||||
|     else if (predictor) | ||||
|     { | ||||
|       // Using a PNG predictor function | ||||
|       st->predictor = (_pdfio_predictor_t)predictor; | ||||
|       st->pbpixel   = (size_t)(bpc * colors + 7) / 8; | ||||
|       st->pbsize    = (size_t)(bpc * colors * columns + 7) / 8; | ||||
|       if (predictor >= 10) | ||||
| 	st->pbsize ++;		// Add PNG predictor byte | ||||
|  | ||||
|       if ((st->pbuffers[0] = calloc(1, st->pbsize)) == NULL || (st->pbuffers[1] = calloc(1, st->pbsize)) == NULL) | ||||
|       { | ||||
| 	_pdfioFileError(st->pdf, "Unable to allocate %lu bytes for Predictor buffers.", (unsigned long)st->pbsize); | ||||
| 	free(st->pbuffers[0]); | ||||
| 	free(st->pbuffers[1]); | ||||
| 	free(st); | ||||
| 	return (NULL); | ||||
|       } | ||||
|     } | ||||
|     else | ||||
|       st->predictor = _PDFIO_PREDICTOR_NONE; | ||||
|  | ||||
|     st->flate.next_out  = (Bytef *)st->cbuffer; | ||||
|     st->flate.avail_out = (uInt)sizeof(st->cbuffer); | ||||
|  | ||||
|     if (deflateInit(&(st->flate), 9) != Z_OK) | ||||
|     { | ||||
|       _pdfioFileError(st->pdf, "Unable to start Flate filter."); | ||||
|       free(st->pbuffers[0]); | ||||
|       free(st->pbuffers[1]); | ||||
|       free(st); | ||||
|       return (NULL); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return (st); | ||||
| } | ||||
|  | ||||
|  | ||||
| // | ||||
| // 'pdfioStreamConsume()' - Consume bytes from the stream. | ||||
| @@ -268,13 +441,8 @@ _pdfioStreamOpen(pdfio_obj_t *obj,	// I - Object | ||||
|       else | ||||
|         st->predictor = _PDFIO_PREDICTOR_NONE; | ||||
|  | ||||
|       st->flate.zalloc    = (alloc_func)0; | ||||
|       st->flate.zfree     = (free_func)0; | ||||
|       st->flate.opaque    = (voidpf)0; | ||||
|       st->flate.next_in  = (Bytef *)st->cbuffer; | ||||
|       st->flate.next_out  = NULL; | ||||
|       st->flate.avail_in = (uInt)_pdfioFileRead(st->pdf, st->cbuffer, sizeof(st->cbuffer)); | ||||
|       st->flate.avail_out = 0; | ||||
|  | ||||
|       if (inflateInit(&(st->flate)) != Z_OK) | ||||
|       { | ||||
| @@ -474,14 +642,112 @@ pdfioStreamWrite( | ||||
|     const void     *buffer,		// I - Data to write | ||||
|     size_t         bytes)		// I - Number of bytes to write | ||||
| { | ||||
|   size_t		pbpixel = st->pbpixel, | ||||
| 					// Size of pixel in bytes | ||||
|       			pbline = st->pbsize - 1, | ||||
| 					// Bytes per line | ||||
| 			remaining,	// Remaining bytes on this line | ||||
| 			firstcol = pbline - pbpixel; | ||||
| 					// First column bytes remaining | ||||
|   const unsigned char	*bufptr = (const unsigned char *)buffer; | ||||
| 					// Pointer into buffer | ||||
|   unsigned char		*thisptr,	// Current raw buffer | ||||
| 			*prevptr;	// Previous raw buffer | ||||
|  | ||||
|  | ||||
|   // Range check input... | ||||
|   if (!st || st->pdf->mode != _PDFIO_MODE_WRITE || !buffer || !bytes) | ||||
|     return (false); | ||||
|  | ||||
|   // TODO: Implement pdfioStreamWrite | ||||
|   // Write it... | ||||
|   if (st->filter == PDFIO_FILTER_NONE) | ||||
|   { | ||||
|     // No filtering so just write it... | ||||
|     return (_pdfioFileWrite(st->pdf, buffer, bytes)); | ||||
|   } | ||||
|  | ||||
|   if (st->predictor == _PDFIO_PREDICTOR_NONE) | ||||
|   { | ||||
|     // No predictor, just write it out straight... | ||||
|     return (stream_write(st, buffer, bytes)); | ||||
|   } | ||||
|   else if ((bytes % pbline) != 0) | ||||
|   { | ||||
|     _pdfioFileError(st->pdf, "Write buffer size must be a multiple of a complete row."); | ||||
|     return (false); | ||||
|   } | ||||
|  | ||||
|   while (bytes > 0) | ||||
|   { | ||||
|     // Store the PNG predictor in the first byte of the buffer... | ||||
|     if (st->predictor == _PDFIO_PREDICTOR_PNG_AUTO) | ||||
|       st->pbuffers[st->pbcurrent][0] = 4; | ||||
|     else | ||||
|       st->pbuffers[st->pbcurrent][0] = (unsigned char)(st->predictor - 10); | ||||
|  | ||||
|     // Then process the current line using the specified PNG predictor... | ||||
|     thisptr = st->pbuffers[st->pbcurrent] + 1; | ||||
|     prevptr = st->pbuffers[!st->pbcurrent] + 1; | ||||
|  | ||||
|     switch (st->predictor) | ||||
|     { | ||||
|       default : | ||||
|           // Anti-compiler-warning code (NONE is handled above, TIFF is not supported for writing) | ||||
| 	  return (false); | ||||
|  | ||||
|       case _PDFIO_PREDICTOR_PNG_SUB : | ||||
| 	  // Encode the difference from the previous column | ||||
| 	  for (remaining = pbline; remaining > 0; remaining --, bufptr ++, thisptr ++) | ||||
| 	  { | ||||
| 	    if (remaining < firstcol) | ||||
| 	      *thisptr = *bufptr - thisptr[-pbpixel]; | ||||
| 	    else | ||||
| 	      *thisptr = *bufptr; | ||||
| 	  } | ||||
| 	  break; | ||||
|  | ||||
|       case _PDFIO_PREDICTOR_PNG_UP : | ||||
| 	  // Encode the difference from the previous line | ||||
| 	  for (remaining = pbline; remaining > 0; remaining --, bufptr ++, thisptr ++, prevptr ++) | ||||
| 	  { | ||||
| 	    *thisptr = *bufptr - *prevptr; | ||||
| 	  } | ||||
| 	  break; | ||||
|  | ||||
|       case _PDFIO_PREDICTOR_PNG_AVERAGE : | ||||
|           // Encode the difference with the average of the previous column and line | ||||
| 	  for (remaining = pbline; remaining > 0; remaining --, bufptr ++, thisptr ++, prevptr ++) | ||||
| 	  { | ||||
| 	    if (remaining < firstcol) | ||||
| 	      *thisptr = *bufptr - (thisptr[-pbpixel] + *prevptr) / 2; | ||||
| 	    else | ||||
| 	      *thisptr = *bufptr - *prevptr / 2; | ||||
| 	  } | ||||
| 	  break; | ||||
|  | ||||
|       case _PDFIO_PREDICTOR_PNG_PAETH : | ||||
|       case _PDFIO_PREDICTOR_PNG_AUTO : | ||||
|           // Encode the difference with a linear transform function | ||||
| 	  for (remaining = pbline; remaining > 0; remaining --, bufptr ++, thisptr ++, prevptr ++) | ||||
| 	  { | ||||
| 	    if (remaining < firstcol) | ||||
| 	      *thisptr = *bufptr - stream_paeth(thisptr[-pbpixel], *prevptr, prevptr[-pbpixel]); | ||||
| 	    else | ||||
| 	      *thisptr = *bufptr - stream_paeth(0, *prevptr, 0); | ||||
| 	  } | ||||
| 	  break; | ||||
|     } | ||||
|  | ||||
|     // Write the encoded line... | ||||
|     if (!stream_write(st, st->pbuffers[st->pbcurrent], st->pbsize)) | ||||
|       return (false); | ||||
|  | ||||
|     st->pbcurrent = !st->pbcurrent; | ||||
|   } | ||||
|  | ||||
|   return (true); | ||||
| } | ||||
|  | ||||
|  | ||||
| // | ||||
| // 'stream_paeth()' - PaethPredictor function for PNG decompression filter. | ||||
| @@ -674,3 +940,44 @@ stream_read(pdfio_stream_t *st,		// I - Stream | ||||
|   return (-1); | ||||
| } | ||||
|  | ||||
|  | ||||
| // | ||||
| // 'stream_write()' - Write flate-compressed data... | ||||
| // | ||||
|  | ||||
| static bool				// O - `true` on success, `false` on failure | ||||
| stream_write(pdfio_stream_t *st,	// I - Stream | ||||
|              const void     *buffer,	// I - Buffer to write | ||||
|              size_t         bytes)	// I - Number of bytes to write | ||||
| { | ||||
|   int	status;				// Compression status | ||||
|  | ||||
|  | ||||
|   // Flate-compress the buffer... | ||||
|   st->flate.avail_in = (uInt)bytes; | ||||
|   st->flate.next_in  = (Bytef *)buffer; | ||||
|  | ||||
|   while (st->flate.avail_in > 0) | ||||
|   { | ||||
|     if (st->flate.avail_out < (sizeof(st->cbuffer) / 8)) | ||||
|     { | ||||
|       // Flush the compression buffer... | ||||
|       if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out)) | ||||
|         return (false); | ||||
|  | ||||
|       st->flate.next_out  = (Bytef *)st->cbuffer; | ||||
|       st->flate.avail_out = sizeof(st->cbuffer); | ||||
|     } | ||||
|  | ||||
|     // Deflate what we can this time... | ||||
|     status = deflate(&st->flate, Z_NO_FLUSH); | ||||
|  | ||||
|     if (status < Z_OK && status != Z_BUF_ERROR) | ||||
|     { | ||||
|       _pdfioFileError(st->pdf, "Flate compression failed (%d)", status); | ||||
|       return (false); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return (true); | ||||
| } | ||||
|   | ||||
| @@ -349,7 +349,8 @@ _pdfioValueRead(pdfio_file_t   *pdf,	// I - PDF file | ||||
|  | ||||
| bool					// O - `true` on success, `false` on failure | ||||
| _pdfioValueWrite(pdfio_file_t   *pdf,	// I - PDF file | ||||
|                  _pdfio_value_t *v)	// I - Value | ||||
|                  _pdfio_value_t *v,	// I - Value | ||||
|                  off_t          *length)// O - Offset to /Length value, if any | ||||
| { | ||||
|   switch (v->type) | ||||
|   { | ||||
| @@ -394,7 +395,7 @@ _pdfioValueWrite(pdfio_file_t   *pdf,	// I - PDF file | ||||
|         } | ||||
|  | ||||
|     case PDFIO_VALTYPE_DICT : | ||||
|         return (_pdfioDictWrite(v->value.dict, NULL)); | ||||
|         return (_pdfioDictWrite(v->value.dict, length)); | ||||
|  | ||||
|     case PDFIO_VALTYPE_INDIRECT : | ||||
|         return (_pdfioFilePrintf(pdf, " %lu %u R", (unsigned long)v->value.indirect.number, v->value.indirect.generation)); | ||||
|   | ||||
							
								
								
									
										5
									
								
								pdfio.h
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								pdfio.h
									
									
									
									
									
								
							| @@ -147,9 +147,10 @@ extern bool		pdfioDictSetString(pdfio_dict_t *dict, const char *key, const char | ||||
| extern bool		pdfioDictSetStringf(pdfio_dict_t *dict, const char *key, const char *format, ...) PDFIO_PUBLIC PDFIO_FORMAT(3,4); | ||||
|  | ||||
| extern bool		pdfioFileClose(pdfio_file_t *pdf) PDFIO_PUBLIC; | ||||
| extern pdfio_file_t	*pdfioFileCreate(const char *filename, const char *version, pdfio_error_cb_t error_cb, void *error_data) PDFIO_PUBLIC; | ||||
| extern pdfio_file_t	*pdfioFileCreate(const char *filename, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) PDFIO_PUBLIC; | ||||
| extern pdfio_obj_t	*pdfioFileCreateObject(pdfio_file_t *pdf, pdfio_dict_t *dict) PDFIO_PUBLIC; | ||||
| extern pdfio_obj_t	*pdfioFileCreatePage(pdfio_file_t *pdf, pdfio_dict_t *dict) PDFIO_PUBLIC; | ||||
| // TODO: Add number, array, string, etc. versions of pdfioFileCreateObject? | ||||
| extern pdfio_stream_t	*pdfioFileCreatePage(pdfio_file_t *pdf, pdfio_dict_t *dict) PDFIO_PUBLIC; | ||||
| extern pdfio_obj_t	*pdfioFileFindObject(pdfio_file_t *pdf, size_t number) PDFIO_PUBLIC; | ||||
| extern pdfio_array_t	*pdfioFileGetID(pdfio_file_t *pdf) PDFIO_PUBLIC; | ||||
| extern const char	*pdfioFileGetName(pdfio_file_t *pdf) PDFIO_PUBLIC; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user