Skip to content

Commit f04ccca

Browse files
committed
Change metadata write order for JPEG output to better follow most common order in JPEG streams. Addresses part of issues raised in #286.
Add new MAX_EXIF start-up parameter that enables control over whether and how EXIF data is embedded in tile requests. Default size is 65533 bytes. 0 = disable, -1 = always embed.
1 parent aff6f7b commit f04ccca

7 files changed

Lines changed: 84 additions & 21 deletions

File tree

ChangeLog

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
27/05/2026:
2+
- Change metadata write order for JPEG output to better follow most common order in JPEG streams.
3+
Addresses part of issues raised in https://github.com/ruven/iipsrv/issues/286.
4+
- Add new MAX_EXIF start-up parameter that enables control over whether and how EXIF data is embedded in tile requests.
5+
Default size is 65533 bytes. 0 = disable, -1 = always embed.
6+
7+
18
11/02/2026:
29
- Correct typos in AVIFCompressor class
310
- Remove obsolete calculateResolution() function from View class

README

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,8 @@ CACHE_CONTROL: Set the HTTP Cache-Control header. See http://www.w3.org/Protocol
453453

454454
MAX_ICC: Set the maximum ICC profile size in bytes that is allowed to be embedded within an output image. This is set by default to 65535 bytes. If set to -1, no limit is set and all profiles are embedded. If set to 0, no profiles are embedded.
455455

456+
MAX_EXIF: Set the maximum EXIF size in bytes that is allowed to be embedded within an output image (when serving tiles). This is set by default to 65532 bytes. If set to -1, no limit is set and all EXIF metadata is embedded. If set to 0, no EXIF metadata is embedded.
457+
456458
OMP_NUM_THREADS: Set the number of OpenMP threads to be used by the iipsrv image processing routines (See OpenMP specification for details). All available processor threads are used by default.
457459

458460
KAKADU_READMODE: Set the Kakadu JPEG2000 read-mode. 0 for 'fast' mode with minimal error checking (default), 1 for 'fussy' mode with no error recovery, 2 for 'resilient' mode with maximum recovery from codestream errors. See the Kakadu documentation for further details.

src/Environment.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
IIP Environment Variable Class
33
4-
Copyright (C) 2006-2025 Ruven Pillay
4+
Copyright (C) 2006-2026 Ruven Pillay
55
66
This program is free software; you can redistribute it and/or modify
77
it under the terms of the GNU General Public License as published by
@@ -51,7 +51,8 @@
5151
#define CACHE_CONTROL "max-age=86400"; // 24 hours
5252
#define ALLOW_UPSCALING true
5353
#define URI_MAP ""
54-
#define MAX_ICC 65536 // Max ICC profile size of 65k
54+
#define MAX_ICC 65536 // Max ICC profile size of 65k
55+
#define MAX_EXIF 65533 // Maximum size possible in a single JPEG chunk (taking into account prefix)
5556
#define CODEC_PASSTHROUGH true
5657
#define KAKADU_READMODE 0
5758
#define IIIF_VERSION 3
@@ -393,6 +394,17 @@ class Environment {
393394
}
394395

395396

397+
static int getMaxEXIF(){
398+
const char* envpara = getenv( "MAX_EXIF" );
399+
int max_exif;
400+
if( envpara ){
401+
max_exif = atoi( envpara );
402+
}
403+
else max_exif = MAX_EXIF;
404+
return max_exif;
405+
}
406+
407+
396408
static bool getCodecPassthrough(){
397409
const char* envpara = getenv( "CODEC_PASSTHROUGH" );
398410
bool codec_passthrough;

src/JPEGCompressor.cc

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* JPEG class wrapper to ijg jpeg library
22
3-
Copyright (C) 2000-2024 Ruven Pillay
3+
Copyright (C) 2000-2026 Ruven Pillay
44
55
This program is free software; you can redistribute it and/or modify
66
it under the terms of the GNU General Public License as published by
@@ -220,18 +220,18 @@ void JPEGCompressor::InitCompression( const RawTile& rawtile, unsigned int strip
220220

221221
jpeg_start_compress( &cinfo, TRUE );
222222

223-
// Add an identifying comment
224-
const char *comment = "iipsrv/" VERSION;
225-
jpeg_write_marker( &cinfo, JPEG_COM, (const JOCTET*) comment, strlen(comment) );
226-
227-
// Embed ICC profile if one is supplied
228-
writeICCProfile();
223+
// Add EXIF metadata
224+
writeExifMetadata();
229225

230226
// Add XMP metadata
231227
writeXMPMetadata();
232228

233-
// Add EXIF metadata
234-
writeExifMetadata();
229+
// Embed ICC profile if one is supplied
230+
writeICCProfile();
231+
232+
// Add an identifying comment
233+
const char *comment = "iipsrv/" VERSION;
234+
jpeg_write_marker( &cinfo, JPEG_COM, (const JOCTET*) comment, strlen(comment) );
235235

236236
// Copy the encoded JPEG header data to a separate buffer
237237
size_t datacount = dest->source_size - dest->pub.free_in_buffer;
@@ -370,20 +370,19 @@ unsigned int JPEGCompressor::Compress( RawTile& rawtile )
370370
jpeg_set_quality( &cinfo, Q, TRUE );
371371

372372
jpeg_start_compress( &cinfo, TRUE );
373-
374-
// Add an identifying comment
375-
const char *comment = "iipsrv/" VERSION;
376-
jpeg_write_marker( &cinfo, JPEG_COM, (const JOCTET*) comment, strlen(comment) );
377373

378-
// Embed ICC profile if one is supplied
379-
writeICCProfile();
374+
// Add EXIF metadata
375+
writeExifMetadata();
380376

381377
// Add XMP metadata
382378
writeXMPMetadata();
383379

384-
// Add EXIF metadata
385-
writeExifMetadata();
380+
// Embed ICC profile if one is supplied
381+
writeICCProfile();
386382

383+
// Add an identifying comment
384+
const char *comment = "iipsrv/" VERSION;
385+
jpeg_write_marker( &cinfo, JPEG_COM, (const JOCTET*) comment, strlen(comment) );
387386

388387
// Compress the image line by line
389388
JSAMPROW row[1];

src/JTL.cc

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
IIP JTL Command Handler Class Member Function: Export a single tile
33
4-
Copyright (C) 2006-2025 Ruven Pillay.
4+
Copyright (C) 2006-2026 Ruven Pillay.
55
66
This program is free software; you can redistribute it and/or modify
77
it under the terms of the GNU General Public License as published by
@@ -169,6 +169,25 @@ void JTL::send( Session* session, int resolution, int tile ){
169169
}
170170

171171

172+
// Embed EXIF metadata if we have one and if embedding has been enabled at start-up
173+
if( ( session->view->maxEXIF() != 0 ) != 0 && ( (*session->image)->getMetadata("exif").size() > 0 ) ){
174+
// Only embed if EXIF is of an acceptable size or if acceptable size is unlimited (-1)
175+
if( ( session->view->maxEXIF() == -1 ) || ( (*session->image)->getMetadata("exif").size() < (unsigned long)session->view->maxEXIF() ) ){
176+
if( session->loglevel >= 3 ){
177+
*(session->logfile) << "JTL :: Embedding EXIF metadata with size "
178+
<< (*session->image)->getMetadata("exif").size() << " bytes" << endl;
179+
}
180+
compressor->embedExifMetadata( true );
181+
}
182+
else{
183+
if( session->loglevel >= 3 ){
184+
*(session->logfile) << "JTL :: EXIF metadata with size "
185+
<< (*session->image)->getMetadata("exif").size() << " bytes is too large: Not embedding" << endl;
186+
}
187+
}
188+
}
189+
190+
172191
RawTile rawtile = tilemanager.getTile( resolution, tile, session->view->xangle,
173192
session->view->yangle, session->view->getLayers(), ct );
174193

src/Main.cc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
IIP FCGI server module - Main loop.
33
4-
Copyright (C) 2000-2025 Ruven Pillay
4+
Copyright (C) 2000-2026 Ruven Pillay
55
66
This program is free software; you can redistribute it and/or modify
77
it under the terms of the GNU General Public License as published by
@@ -349,6 +349,10 @@ int main( int argc, char *argv[] )
349349
int max_icc = Environment::getMaxICC();
350350

351351

352+
// Get the max EXIF size we allow to be embedded
353+
int max_exif = Environment::getMaxEXIF();
354+
355+
352356
// Get codec passthrough setting
353357
IIPImage::codec_passthrough = Environment::getCodecPassthrough();
354358

@@ -437,6 +441,9 @@ int main( int argc, char *argv[] )
437441
logfile << "Setting maximum ICC profile size to ";
438442
if( max_icc < 0 ) logfile << "unlimited" << endl;
439443
else logfile << max_icc << " bytes" << endl;
444+
logfile << "Setting maximum EXIF size to ";
445+
if( max_exif < 0 ) logfile << "unlimited" << endl;
446+
else logfile << max_exif << " bytes" << endl;
440447
logfile << "Setting codec passthrough to " << (IIPImage::codec_passthrough? "true" : "false") << endl;
441448
if( !copyright.empty() ) logfile << "Setting default rights/copyright statement to '" << copyright << "'" << endl;
442449
logfile << "Setting up TIFF support via " << TPTImage::getCodecVersion() << endl;
@@ -666,6 +673,7 @@ int main( int argc, char *argv[] )
666673
if( max_layers != 0 ) view.setMaxLayers( max_layers );
667674
view.setAllowUpscaling( allow_upscaling );
668675
view.setMaxICC( max_icc );
676+
view.setMaxEXIF( max_exif );
669677

670678

671679
// Create an IIPResponse object - we use this for the OBJ requests.

src/View.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class View{
6969
bool maintain_aspect; ///< Indicate whether aspect ratio should be maintained
7070
bool allow_upscaling; ///< Indicate whether images may be served larger than the source file
7171
int max_icc; ///< Maximum ICC profile size we allow to be embedded
72+
int max_exif; ///< Maximum EXIF size we allow to be embedded
7273
ImageEncoding output_format; ///< Requested output format
7374
float contrast; ///< Contrast adjustment requested by CNT command
7475
float gamma; ///< Gamma adjustment requested by GAM command
@@ -95,6 +96,7 @@ class View{
9596
allow_upscaling = true;
9697
colorspace = ColorSpace::NONE;
9798
max_icc = -1;
99+
max_exif = -1;
98100
output_format = ImageEncoding::JPEG;
99101
equalization = false;
100102
minmax = false;
@@ -142,6 +144,20 @@ class View{
142144
}
143145

144146

147+
/// Set the maximum EXIF size we allow to be embedded
148+
/** @param max maximum icc profile size
149+
*/
150+
void setMaxEXIF( int max ){ max_exif = max; };
151+
152+
153+
/// Get the maximum EXIF size we allow to be embedded
154+
/** @return max EXIFsize
155+
*/
156+
int maxEXIF(){
157+
return max_exif;
158+
}
159+
160+
145161
/// Set the maximum view port dimension
146162
/** @param r number of availale resolutions */
147163
void setMaxResolutions( unsigned int r ){ max_resolutions = r; resolution=r-1; };

0 commit comments

Comments
 (0)