Skip to content

Commit 917fe71

Browse files
committed
Re-factoring of metadata handling in output compressor classes and addition of more complete metadata embedding for PNG.
1 parent 70080cc commit 917fe71

12 files changed

+221
-132
lines changed

ChangeLog

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
29/06/2024:
22
- Updated autoconf tiff m4 to search for tiffio.h rather than just tiff.h when using TIFFOpen().
33
- Switched metadata assignment within input image classes to use more efficient map insert() syntax.
4+
- Re-factoring of metadata handling in output compressor classes and addition of more complete metadata
5+
embedding for PNG.
46

57

68
29/05/2024:

src/CVT.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,10 @@ void CVT::send( Session* session ){
502502
}
503503

504504

505+
// Add metadata
506+
compressor->setMetadata( (*session->image)->metadata );
507+
508+
505509
// Set the physical output resolution for this particular view and zoom level
506510
if( (*session->image)->dpi_x > 0 && (*session->image)->dpi_y > 0 ){
507511
float dpi_x = (*session->image)->dpi_x * (float) im_width / (float) (*session->image)->getImageWidth();
@@ -513,14 +517,14 @@ void CVT::send( Session* session ){
513517
}
514518
}
515519

516-
// Set ICC profile if of a reasonable size
520+
// Embed ICC profile if of a reasonable size
517521
if( session->view->embedICC() && ((*session->image)->getMetadata("icc").size()>0) ){
518522
if( (*session->image)->getMetadata("icc").size() < 65536 ){
519523
if( session->loglevel >= 3 ){
520524
*(session->logfile) << "CVT :: Embedding ICC profile with size "
521525
<< (*session->image)->getMetadata("icc").size() << " bytes" << endl;
522526
}
523-
compressor->setICCProfile( (*session->image)->getMetadata("icc") );
527+
compressor->embedICCProfile( true );
524528
}
525529
else{
526530
if( session->loglevel >= 3 ){
@@ -530,13 +534,13 @@ void CVT::send( Session* session ){
530534
}
531535
}
532536

533-
// Add XMP metadata if this exists
537+
// Always embed XMP metadata in CVT function
534538
if( (*session->image)->getMetadata("xmp").size() > 0 ){
535539
if( session->loglevel >= 3 ){
536540
*(session->logfile) << "CVT :: Embedding XMP metadata with size "
537541
<< (*session->image)->getMetadata("xmp").size() << " bytes" << endl;
538542
}
539-
compressor->setXMPMetadata( (*session->image)->getMetadata("xmp") );
543+
compressor->embedXMPMetadata( true );
540544
}
541545

542546

src/Compressor.h

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* Generic compressor class - extended by JPEG and PNG Compressor classes
22
3-
Copyright (C) 2017-2023 Ruven Pillay
3+
Copyright (C) 2017-2024 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
@@ -23,9 +23,8 @@
2323

2424

2525

26-
#include <string>
2726
#include "RawTile.h"
28-
27+
#include <map>
2928

3029

3130
/// Base class for IIP output images
@@ -52,12 +51,23 @@ class Compressor {
5251
/** Units can be 0 for unknown, 1 for dots/inch or 2 for dots/cm */
5352
int dpi_units;
5453

54+
/// Metadata
55+
std::map <const std::string, const std::string> metadata;
56+
5557
/// ICC Profile
58+
bool embedICC;
5659
std::string icc;
5760

5861
/// XMP metadata
62+
bool embedXMP;
5963
std::string xmp;
6064

65+
/// Write metadata
66+
virtual void writeMetadata() {};
67+
68+
/// Write DPI
69+
virtual void writeResolution() {};
70+
6171
/// Write ICC profile
6272
virtual void writeICCProfile() {};
6373

@@ -76,12 +86,24 @@ class Compressor {
7686
header_size( 0 ),
7787
dpi_x( 0 ),
7888
dpi_y( 0 ),
79-
dpi_units( 0 ) {};
89+
dpi_units( 0 ),
90+
embedICC( false ),
91+
embedXMP( false ) {};
8092

8193

8294
virtual ~Compressor() {};
8395

8496

97+
/// Return the image header size
98+
/** @return header size in bytes */
99+
unsigned int getHeaderSize() const { return header_size; };
100+
101+
102+
/// Return a pointer to the image header itself
103+
/** @return binary header blob */
104+
unsigned char* getHeader() { return header; };
105+
106+
85107
/// Get the current quality level
86108
inline int getQuality() const { return Q; }
87109

@@ -94,24 +116,38 @@ class Compressor {
94116
inline void setResolution( float x, float y, int units ){ dpi_x = x; dpi_y = y; dpi_units = units; };
95117

96118

97-
/// Set the ICC profile
98-
/** @param profile ICC profile string */
99-
inline void setICCProfile( const std::string& profile ){ icc = profile; }
119+
/// Embed ICC profile
120+
/** @param embed Whether ICC profile should be embedded */
121+
inline void embedICCProfile( const bool embed ){ this->embedICC = embed; }
100122

101123

102-
/// Set XMP metadata
103-
/** @param x XMP metadata string */
104-
inline void setXMPMetadata( const std::string& x ){ xmp = x; }
124+
/// Embed XMP metadata
125+
/** @param embed Whether XMP metadata should be embedded */
126+
inline void embedXMPMetadata( const bool embed ){ this->embedXMP = embed; }
105127

106128

107-
/// Return the image header size
108-
/** @return header size in bytes */
109-
virtual unsigned int getHeaderSize() const { return 0; };
129+
/// Set general metadata
130+
/** @param metadata Metadata list */
131+
inline void setMetadata( const std::map <const std::string, const std::string>& metadata )
132+
{
133+
this->metadata = std::map <const std::string, const std::string>( metadata );
110134

135+
// Extract ICC profile if it exists and remove from list
136+
std::map<const std::string, const std::string> :: const_iterator it;
137+
it = this->metadata.find("icc");
138+
if( it != this->metadata.end() ){
139+
icc = it->second;
140+
this->metadata.erase( it );
141+
}
142+
143+
// Extract XMP chunk if it exists and remove from list
144+
it = this->metadata.find("xmp");
145+
if( it != this->metadata.end() ){
146+
xmp = it->second;
147+
this->metadata.erase( it );
148+
}
149+
};
111150

112-
/// Return a pointer to the image header itself
113-
/** @return binary header blob */
114-
virtual unsigned char* getHeader() { return NULL; };
115151

116152

117153
/// Initialise strip based compression
@@ -147,11 +183,6 @@ class Compressor {
147183
virtual unsigned int Compress( RawTile& t ) { return 0; };
148184

149185

150-
/// Add metadata to the image header
151-
/** @param m metadata */
152-
virtual void addXMPMetadata( const std::string& m ) {};
153-
154-
155186
/// Get mime type
156187
/** @return IANA mime type as const char* */
157188
virtual const char* getMimeType() const { return "image/example"; };

src/IIPImage.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ class IIPImage {
174174
std::vector<unsigned int> histogram;
175175

176176
/// STL map to hold string metadata
177-
std::map <const std::string, std::string> metadata;
177+
std::map <const std::string, const std::string> metadata;
178178

179179
/// Image modification timestamp
180180
time_t timestamp;

src/JPEGCompressor.cc

Lines changed: 20 additions & 14 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-2023 Ruven Pillay.
3+
Copyright (C) 2000-2024 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
@@ -228,10 +228,8 @@ void JPEGCompressor::InitCompression( const RawTile& rawtile, unsigned int strip
228228
cinfo.in_color_space = ( channels == 3 ? JCS_RGB : JCS_GRAYSCALE );
229229
jpeg_set_defaults( &cinfo );
230230

231-
// Set our physical output resolution (JPEG only supports integers)
232-
cinfo.X_density = round( dpi_x );
233-
cinfo.Y_density = round( dpi_y );
234-
cinfo.density_unit = dpi_units;
231+
// Add DPI
232+
writeResolution();
235233

236234
// Set compression point quality (highest, but possibly slower depending
237235
// on hardware) - must do this after we've set the defaults!
@@ -402,10 +400,8 @@ unsigned int JPEGCompressor::Compress( RawTile& rawtile )
402400
cinfo.in_color_space = ( channels == 3 ? JCS_RGB : JCS_GRAYSCALE );
403401
jpeg_set_defaults( &cinfo );
404402

405-
// Set our physical output resolution (JPEG only supports integers)
406-
if( dpi_x ) cinfo.X_density = round( dpi_x );
407-
if( dpi_y ) cinfo.Y_density = round( dpi_y );
408-
if( dpi_x || dpi_y ) cinfo.density_unit = dpi_units;
403+
// Add DPI
404+
writeResolution();
409405

410406
// Set compression quality (fastest, but possibly slower depending
411407
// on hardware) - must do this after we've set the defaults!
@@ -468,6 +464,17 @@ unsigned int JPEGCompressor::Compress( RawTile& rawtile )
468464

469465

470466

467+
// Write DPI information
468+
void JPEGCompressor::writeResolution()
469+
{
470+
// Set our physical output resolution (JPEG only supports integers)
471+
if( dpi_x ) cinfo.X_density = round( dpi_x );
472+
if( dpi_y ) cinfo.Y_density = round( dpi_y );
473+
if( dpi_x || dpi_y ) cinfo.density_unit = dpi_units;
474+
}
475+
476+
477+
471478
// Write ICC profile into JPEG header if profile has been set
472479
// Function *must* be called AFTER calling jpeg_start_compress() and BEFORE
473480
// the first call to jpeg_write_scanlines().
@@ -478,14 +485,13 @@ unsigned int JPEGCompressor::Compress( RawTile& rawtile )
478485
// See the copyright notice in COPYING.ijg for details
479486
void JPEGCompressor::writeICCProfile()
480487
{
488+
// Skip if profile embedding disabled
489+
if( !embedICC || icc.empty() ) return;
490+
481491
unsigned int num_markers; // total number of markers we'll write
482492
int cur_marker = 1; // per spec, counting starts at 1
483493
unsigned int length; // number of bytes to write in this marker
484494

485-
// Skip if our profile has zero size or is too big
486-
// if( icc.size() == 0 || icc.size() > MAX_DATA_BYTES_IN_MARKER ) return;
487-
if( icc.empty() ) return;
488-
489495
unsigned int icc_data_len = icc.size();
490496
const char* icc_data_ptr = icc.c_str();
491497

@@ -542,7 +548,7 @@ void JPEGCompressor::writeICCProfile()
542548
void JPEGCompressor::writeXMPMetadata()
543549
{
544550
// Make sure our XMP data has a valid size (namespace prefix is 29 bytes)
545-
if( xmp.empty() || xmp.size()>(65536-XMP_PREFIX_SIZE) ) return;
551+
if( !embedXMP || xmp.empty() || xmp.size()>(65536-XMP_PREFIX_SIZE) ) return;
546552

547553
// The XMP data in a JPEG stream needs to be prefixed with a zero-terminated ID string
548554
// ref http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/cs6/XMPSpecificationPart3.pdf (pp13-14)

src/JPEGCompressor.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* JPEG class wrapper to ijg libjpeg library
22
3-
Copyright (C) 2000-2023 Ruven Pillay.
3+
Copyright (C) 2000-2024 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
@@ -73,6 +73,9 @@ class JPEGCompressor: public Compressor{
7373
iip_destination_mgr dest_mgr;
7474
iip_dest_ptr dest;
7575

76+
/// Write DPI
77+
void writeResolution();
78+
7679
/// Write ICC profile
7780
void writeICCProfile();
7881

@@ -128,12 +131,6 @@ class JPEGCompressor: public Compressor{
128131
/** @param t tile of image data */
129132
unsigned int Compress( RawTile& t );
130133

131-
/// Return the JPEG header size
132-
inline unsigned int getHeaderSize() const { return header_size; }
133-
134-
/// Return a pointer to the header itself
135-
inline unsigned char* getHeader() { return header; }
136-
137134
/// Return the JPEG mime type
138135
inline const char* getMimeType() const { return "image/jpeg"; }
139136

src/JTL.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,16 +141,21 @@ void JTL::send( Session* session, int resolution, int tile ){
141141
}
142142

143143

144+
// Add metadata
145+
compressor->setMetadata( (*session->image)->metadata );
146+
147+
144148
// Embed ICC profile
145-
if( session->view->embedICC() && ((*session->image)->getMetadata("icc").size()>0) ){
149+
if( session->view->embedICC() && (*session->image)->metadata["icc"].size() > 0 ){
146150
if( session->loglevel >= 3 ){
147151
*(session->logfile) << "JTL :: Embedding ICC profile with size "
148152
<< (*session->image)->getMetadata("icc").size() << " bytes" << endl;
149153
}
150-
compressor->setICCProfile( (*session->image)->getMetadata("icc") );
154+
compressor->embedICCProfile( true );
151155
}
152156

153157

158+
154159
RawTile rawtile = tilemanager.getTile( resolution, tile, session->view->xangle,
155160
session->view->yangle, session->view->getLayers(), ct );
156161

src/OBJ.cc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,8 @@ void OBJ::run( Session* s, const std::string& a )
112112
stringstream json;
113113
json << "{ ";
114114

115-
map <const string, string> metadata = (*session->image)->metadata;
116-
map<const string,string> :: const_iterator i;
117-
for( i = metadata.begin(); i != metadata.end(); i++ ){
115+
map <const string, const string> :: const_iterator i;
116+
for( i = (*session->image)->metadata.begin(); i != (*session->image)->metadata.end(); i++ ){
118117
if( i->first == "icc" || i->first == "xmp" ) continue;
119118
if( (i->second).length() ) json << endl << "\t\"" << i->first << "\": \"" << i->second << "\",";
120119
}

0 commit comments

Comments
 (0)