1 // ***************************************************************** -*- C++ -*- 2 /* 3 * Copyright (C) 2004-2017 Andreas Huggel <ahuggel@gmx.net> 4 * 5 * This program is part of the Exiv2 distribution. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA. 20 */ 21 /* 22 File: epsimage.cpp 23 Version: $Rev: 2455 $ 24 Author(s): Michael Ulbrich (mul) <mul@rentapacs.de> 25 Volker Grabsch (vog) <vog@notjusthosting.com> 26 History: 7-Mar-2011, vog: created 27 */ 28 // ***************************************************************************** 29 #include "rcsid_int.hpp" 30 EXIV2_RCSID("@(#) $Id: epsimage.cpp $") 31 32 // included header files 33 #include "config_exiv2.h" 34 35 #include "epsimage.hpp" 36 #include "image.hpp" 37 #include "basicio.hpp" 38 #include "error.hpp" 39 #include "futils.hpp" 40 41 // + standard includes 42 #include <algorithm> 43 #include <cassert> 44 #include <climits> 45 #include <cstring> 46 #include <iostream> 47 #include <sstream> 48 #include <string> 49 50 // ***************************************************************************** 51 namespace { 52 53 using namespace Exiv2; 54 using Exiv2::byte; 55 56 // signature of DOS EPS 57 const std::string dosEpsSignature = "\xC5\xD0\xD3\xC6"; 58 59 // first line of EPS 60 const std::string epsFirstLine[] = { 61 "%!PS-Adobe-3.0 EPSF-3.0", 62 "%!PS-Adobe-3.0 EPSF-3.0 ", // OpenOffice 63 "%!PS-Adobe-3.1 EPSF-3.0", // Illustrator 64 }; 65 66 // blank EPS file 67 const std::string epsBlank = "%!PS-Adobe-3.0 EPSF-3.0\n" 68 "%%BoundingBox: 0 0 0 0\n"; 69 70 // list of all valid XMP headers 71 const std::string xmpHeaders[] = { 72 73 // We do not enforce the trailing "?>" here, because the XMP specification 74 // permits additional attributes after begin="..." and id="...". 75 76 // normal headers 77 "<?xpacket begin=\"\xef\xbb\xbf\" id=\"W5M0MpCehiHzreSzNTczkc9d\"", 78 "<?xpacket begin=\"\xef\xbb\xbf\" id='W5M0MpCehiHzreSzNTczkc9d'", 79 "<?xpacket begin='\xef\xbb\xbf' id=\"W5M0MpCehiHzreSzNTczkc9d\"", 80 "<?xpacket begin='\xef\xbb\xbf' id='W5M0MpCehiHzreSzNTczkc9d'", 81 82 // deprecated headers (empty begin attribute, UTF-8 only) 83 "<?xpacket begin=\"\" id=\"W5M0MpCehiHzreSzNTczkc9d\"", 84 "<?xpacket begin=\"\" id='W5M0MpCehiHzreSzNTczkc9d'", 85 "<?xpacket begin='' id=\"W5M0MpCehiHzreSzNTczkc9d\"", 86 "<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'", 87 }; 88 89 // list of all valid XMP trailers 90 struct XmpTrailer { 91 std::string trailer; 92 bool readOnly; 93 }; 94 95 const XmpTrailer xmpTrailers[] = { 96 97 // We do not enforce the trailing "?>" here, because the XMP specification 98 // permits additional attributes after end="...". 99 100 {"<?xpacket end=\"r\"", true}, 101 {"<?xpacket end='r'", true}, 102 {"<?xpacket end=\"w\"", false}, 103 {"<?xpacket end='w'", false}, 104 }; 105 106 // closing part of all valid XMP trailers 107 const std::string xmpTrailerEnd = "?>"; 108 109 //! Write data into temp file, taking care of errors writeTemp(BasicIo & tempIo,const byte * data,size_t size)110 void writeTemp(BasicIo& tempIo, const byte* data, size_t size) 111 { 112 if (size == 0) return; 113 if (tempIo.write(data, static_cast<long>(size)) != static_cast<long>(size)) { 114 #ifndef SUPPRESS_WARNINGS 115 EXV_WARNING << "Failed to write to temporary file.\n"; 116 #endif 117 throw Error(21); 118 } 119 } 120 121 //! Write data into temp file, taking care of errors writeTemp(BasicIo & tempIo,const std::string & data)122 void writeTemp(BasicIo& tempIo, const std::string &data) 123 { 124 writeTemp(tempIo, reinterpret_cast<const byte*>(data.data()), data.size()); 125 } 126 127 //! Get the current write position of temp file, taking care of errors posTemp(BasicIo & tempIo)128 uint32_t posTemp(BasicIo& tempIo) 129 { 130 const long pos = tempIo.tell(); 131 if (pos == -1) { 132 #ifndef SUPPRESS_WARNINGS 133 EXV_WARNING << "Internal error while determining current write position in temporary file.\n"; 134 #endif 135 throw Error(21); 136 } 137 return static_cast<uint32_t>(pos); 138 } 139 140 //! Check whether a string has a certain beginning startsWith(const std::string & s,const std::string & start)141 bool startsWith(const std::string& s, const std::string& start) 142 { 143 return s.size() >= start.size() && memcmp(s.data(), start.data(), start.size()) == 0; 144 } 145 146 //! Check whether a string contains only white space characters onlyWhitespaces(const std::string & s)147 bool onlyWhitespaces(const std::string& s) 148 { 149 // According to the DSC 3.0 specification, 4.4 Parsing Rules, 150 // only spaces and tabs are considered to be white space characters. 151 return s.find_first_not_of(" \t") == std::string::npos; 152 } 153 154 //! Read the next line of a buffer, allow for changing line ending style readLine(std::string & line,const byte * data,size_t startPos,size_t size)155 size_t readLine(std::string& line, const byte* data, size_t startPos, size_t size) 156 { 157 line.clear(); 158 size_t pos = startPos; 159 // step through line 160 while (pos < size && data[pos] != '\r' && data[pos] != '\n') { 161 line += data[pos]; 162 pos++; 163 } 164 // skip line ending, if present 165 if (pos >= size) return pos; 166 pos++; 167 if (pos >= size) return pos; 168 if (data[pos - 1] == '\r' && data[pos] == '\n') pos++; 169 return pos; 170 } 171 172 //! Read the previous line of a buffer, allow for changing line ending style readPrevLine(std::string & line,const byte * data,size_t startPos,size_t size)173 size_t readPrevLine(std::string& line, const byte* data, size_t startPos, size_t size) 174 { 175 line.clear(); 176 size_t pos = startPos; 177 if (pos > size) return pos; 178 // skip line ending of previous line, if present 179 if (pos <= 0) return pos; 180 if (data[pos - 1] == '\r' || data[pos - 1] == '\n') { 181 pos--; 182 if (pos <= 0) return pos; 183 if (data[pos - 1] == '\r' && data[pos] == '\n') { 184 pos--; 185 if (pos <= 0) return pos; 186 } 187 } 188 // step through previous line 189 while (pos >= 1 && data[pos - 1] != '\r' && data[pos - 1] != '\n') { 190 pos--; 191 line += data[pos]; 192 } 193 std::reverse(line.begin(), line.end()); 194 return pos; 195 } 196 197 //! Find an XMP block findXmp(size_t & xmpPos,size_t & xmpSize,const byte * data,size_t startPos,size_t size,bool write)198 void findXmp(size_t& xmpPos, size_t& xmpSize, const byte* data, size_t startPos, size_t size, bool write) 199 { 200 // search for valid XMP header 201 xmpSize = 0; 202 for (xmpPos = startPos; xmpPos < size; xmpPos++) { 203 if (data[xmpPos] != '\x00' && data[xmpPos] != '<') continue; 204 for (size_t i = 0; i < (sizeof xmpHeaders) / (sizeof *xmpHeaders); i++) { 205 const std::string &header = xmpHeaders[i]; 206 if (xmpPos + header.size() > size) continue; 207 if (memcmp(data + xmpPos, header.data(), header.size()) != 0) continue; 208 #ifdef DEBUG 209 EXV_DEBUG << "findXmp: Found XMP header at position: " << xmpPos << "\n"; 210 #endif 211 212 // search for valid XMP trailer 213 for (size_t trailerPos = xmpPos + header.size(); trailerPos < size; trailerPos++) { 214 if (data[xmpPos] != '\x00' && data[xmpPos] != '<') continue; 215 for (size_t j = 0; j < (sizeof xmpTrailers) / (sizeof *xmpTrailers); j++) { 216 const std::string &trailer = xmpTrailers[j].trailer; 217 const bool readOnly = xmpTrailers[j].readOnly; 218 219 if (trailerPos + trailer.size() > size) continue; 220 if (memcmp(data + trailerPos, trailer.data(), trailer.size()) != 0) continue; 221 #ifdef DEBUG 222 EXV_DEBUG << "findXmp: Found XMP trailer at position: " << trailerPos << "\n"; 223 #endif 224 225 if (readOnly) { 226 #ifndef SUPPRESS_WARNINGS 227 EXV_WARNING << "Unable to handle read-only XMP metadata yet. Please provide your " 228 "sample EPS file to the Exiv2 project: http://dev.exiv2.org/projects/exiv2\n"; 229 #endif 230 throw Error(write ? 21 : 14); 231 } 232 233 // search for end of XMP trailer 234 for (size_t trailerEndPos = trailerPos + trailer.size(); trailerEndPos + xmpTrailerEnd.size() <= size; trailerEndPos++) { 235 if (memcmp(data + trailerEndPos, xmpTrailerEnd.data(), xmpTrailerEnd.size()) == 0) { 236 xmpSize = (trailerEndPos + xmpTrailerEnd.size()) - xmpPos; 237 return; 238 } 239 } 240 #ifndef SUPPRESS_WARNINGS 241 EXV_WARNING << "Found XMP header but incomplete XMP trailer.\n"; 242 #endif 243 throw Error(write ? 21 : 14); 244 } 245 } 246 #ifndef SUPPRESS_WARNINGS 247 EXV_WARNING << "Found XMP header but no XMP trailer.\n"; 248 #endif 249 throw Error(write ? 21 : 14); 250 } 251 } 252 } 253 254 //! Unified implementation of reading and writing EPS metadata readWriteEpsMetadata(BasicIo & io,std::string & xmpPacket,NativePreviewList & nativePreviews,bool write)255 void readWriteEpsMetadata(BasicIo& io, std::string& xmpPacket, NativePreviewList& nativePreviews, bool write) 256 { 257 // open input file 258 if (io.open() != 0) { 259 throw Error(9, io.path(), strError()); 260 } 261 IoCloser closer(io); 262 263 // read from input file via memory map 264 const byte *data = io.mmap(); 265 266 // default positions and sizes 267 const size_t size = static_cast<size_t>(io.size()); 268 size_t posEps = 0; 269 size_t posEndEps = size; 270 uint32_t posWmf = 0; 271 uint32_t sizeWmf = 0; 272 uint32_t posTiff = 0; 273 uint32_t sizeTiff = 0; 274 275 // check for DOS EPS 276 const bool dosEps = (size >= dosEpsSignature.size() && memcmp(data, dosEpsSignature.data(), dosEpsSignature.size()) == 0); 277 if (dosEps) { 278 #ifdef DEBUG 279 EXV_DEBUG << "readWriteEpsMetadata: Found DOS EPS signature\n"; 280 #endif 281 if (size < 30) { 282 #ifndef SUPPRESS_WARNINGS 283 EXV_WARNING << "Premature end of file after DOS EPS signature.\n"; 284 #endif 285 throw Error(write ? 21 : 14); 286 } 287 posEps = getULong(data + 4, littleEndian); 288 posEndEps = getULong(data + 8, littleEndian) + posEps; 289 posWmf = getULong(data + 12, littleEndian); 290 sizeWmf = getULong(data + 16, littleEndian); 291 posTiff = getULong(data + 20, littleEndian); 292 sizeTiff = getULong(data + 24, littleEndian); 293 const uint16_t checksum = getUShort(data + 28, littleEndian); 294 #ifdef DEBUG 295 EXV_DEBUG << "readWriteEpsMetadata: EPS section at position " << posEps << ", size " << (posEndEps - posEps) << "\n"; 296 EXV_DEBUG << "readWriteEpsMetadata: WMF section at position " << posWmf << ", size " << sizeWmf << "\n"; 297 EXV_DEBUG << "readWriteEpsMetadata: TIFF section at position " << posTiff << ", size " << sizeTiff << "\n"; 298 #endif 299 if (checksum != 0xFFFF) { 300 #ifdef DEBUG 301 EXV_DEBUG << "readWriteEpsMetadata: DOS EPS checksum is not FFFF\n"; 302 #endif 303 } 304 if (!((posWmf == 0 && sizeWmf == 0) || (posTiff == 0 && sizeTiff == 0))) { 305 #ifndef SUPPRESS_WARNINGS 306 EXV_WARNING << "DOS EPS file has both WMF and TIFF section. Only one of those is allowed.\n"; 307 #endif 308 if (write) throw Error(21); 309 } 310 if (sizeWmf == 0 && sizeTiff == 0) { 311 #ifndef SUPPRESS_WARNINGS 312 EXV_WARNING << "DOS EPS file has neither WMF nor TIFF section. Exactly one of those is required.\n"; 313 #endif 314 if (write) throw Error(21); 315 } 316 if (posEps < 30 || posEndEps > size) { 317 #ifndef SUPPRESS_WARNINGS 318 EXV_WARNING << "DOS EPS file has invalid position (" << posEps << ") or size (" << (posEndEps - posEps) << ") for EPS section.\n"; 319 #endif 320 throw Error(write ? 21 : 14); 321 } 322 if (sizeWmf != 0 && (posWmf < 30 || posWmf + sizeWmf > size)) { 323 #ifndef SUPPRESS_WARNINGS 324 EXV_WARNING << "DOS EPS file has invalid position (" << posWmf << ") or size (" << sizeWmf << ") for WMF section.\n"; 325 #endif 326 if (write) throw Error(21); 327 } 328 if (sizeTiff != 0 && (posTiff < 30 || posTiff + sizeTiff > size)) { 329 #ifndef SUPPRESS_WARNINGS 330 EXV_WARNING << "DOS EPS file has invalid position (" << posTiff << ") or size (" << sizeTiff << ") for TIFF section.\n"; 331 #endif 332 if (write) throw Error(21); 333 } 334 } 335 336 // check first line 337 std::string firstLine; 338 const size_t posSecondLine = readLine(firstLine, data, posEps, posEndEps); 339 #ifdef DEBUG 340 EXV_DEBUG << "readWriteEpsMetadata: First line: " << firstLine << "\n"; 341 #endif 342 bool matched = false; 343 for (size_t i = 0; !matched && i < (sizeof epsFirstLine) / (sizeof *epsFirstLine); i++) { 344 matched = (firstLine == epsFirstLine[i]); 345 } 346 if (!matched) { 347 throw Error(3, "EPS"); 348 } 349 350 // determine line ending style of the first line 351 if (posSecondLine >= posEndEps) { 352 #ifndef SUPPRESS_WARNINGS 353 EXV_WARNING << "Premature end of file after first line.\n"; 354 #endif 355 throw Error(write ? 21 : 14); 356 } 357 const std::string lineEnding(reinterpret_cast<const char*>(data + posEps + firstLine.size()), posSecondLine - (posEps + firstLine.size())); 358 #ifdef DEBUG 359 if (lineEnding == "\n") { 360 EXV_DEBUG << "readWriteEpsMetadata: Line ending style: Unix (LF)\n"; 361 } else if (lineEnding == "\r") { 362 EXV_DEBUG << "readWriteEpsMetadata: Line ending style: Mac (CR)\n"; 363 } else if (lineEnding == "\r\n") { 364 EXV_DEBUG << "readWriteEpsMetadata: Line ending style: DOS (CR LF)\n"; 365 } else { 366 EXV_DEBUG << "readWriteEpsMetadata: Line ending style: (unknown)\n"; 367 } 368 #endif 369 370 // scan comments 371 size_t posLanguageLevel = posEndEps; 372 size_t posContainsXmp = posEndEps; 373 size_t posPages = posEndEps; 374 size_t posExiv2Version = posEndEps; 375 size_t posExiv2Website = posEndEps; 376 size_t posEndComments = posEndEps; 377 size_t posAi7Thumbnail = posEndEps; 378 size_t posAi7ThumbnailEndData = posEndEps; 379 size_t posBeginPhotoshop = posEndEps; 380 size_t posEndPhotoshop = posEndEps; 381 size_t posPage = posEndEps; 382 size_t posBeginPageSetup = posEndEps; 383 size_t posEndPageSetup = posEndEps; 384 size_t posPageTrailer = posEndEps; 385 size_t posEof = posEndEps; 386 std::vector<std::pair<size_t, size_t> > removableEmbeddings; 387 unsigned int depth = 0; 388 const unsigned int maxDepth = UINT_MAX; 389 bool illustrator8 = false; 390 bool corelDraw = false; 391 bool implicitPage = false; 392 bool implicitPageSetup = false; 393 bool implicitPageTrailer = false; 394 bool inDefaultsPreviewPrologSetup = false; 395 bool inRemovableEmbedding = false; 396 std::string removableEmbeddingEndLine; 397 unsigned int removableEmbeddingsWithUnmarkedTrailer = 0; 398 for (size_t pos = posEps; pos < posEof;) { 399 const size_t startPos = pos; 400 std::string line; 401 pos = readLine(line, data, startPos, posEndEps); 402 #ifdef DEBUG 403 bool significantLine = true; 404 #endif 405 // nested documents 406 if (posPage == posEndEps && (startsWith(line, "%%IncludeDocument:") || startsWith(line, "%%BeginDocument:"))) { 407 #ifndef SUPPRESS_WARNINGS 408 EXV_WARNING << "Nested document at invalid position: " << startPos << "\n"; 409 #endif 410 throw Error(write ? 21 : 14); 411 } else if (startsWith(line, "%%BeginDocument:")) { 412 if (depth == maxDepth) { 413 #ifndef SUPPRESS_WARNINGS 414 EXV_WARNING << "Document too deeply nested at position: " << startPos << "\n"; 415 #endif 416 throw Error(write ? 21 : 14); 417 } 418 depth++; 419 } else if (startsWith(line, "%%EndDocument")) { 420 if (depth == 0) { 421 #ifndef SUPPRESS_WARNINGS 422 EXV_WARNING << "Unmatched EndDocument at position: " << startPos << "\n"; 423 #endif 424 throw Error(write ? 21 : 14); 425 } 426 depth--; 427 } else { 428 #ifdef DEBUG 429 significantLine = false; 430 #endif 431 } 432 #ifdef DEBUG 433 if (significantLine) { 434 EXV_DEBUG << "readWriteEpsMetadata: Found significant line \"" << line << "\" at position: " << startPos << "\n"; 435 } 436 significantLine = true; 437 #endif 438 if (depth != 0) continue; 439 // explicit "Begin" comments 440 if (startsWith(line, "%%BeginPreview:")) { 441 inDefaultsPreviewPrologSetup = true; 442 } else if (line == "%%BeginDefaults") { 443 inDefaultsPreviewPrologSetup = true; 444 } else if (line == "%%BeginProlog") { 445 inDefaultsPreviewPrologSetup = true; 446 } else if (line == "%%BeginSetup") { 447 inDefaultsPreviewPrologSetup = true; 448 } else if (posPage == posEndEps && startsWith(line, "%%Page:")) { 449 posPage = startPos; 450 } else if (posPage != posEndEps && startsWith(line, "%%Page:")) { 451 if (implicitPage) { 452 #ifndef SUPPRESS_WARNINGS 453 EXV_WARNING << "Page at position " << startPos << " conflicts with implicit page at position: " << posPage << "\n"; 454 #endif 455 throw Error(write ? 21 : 14); 456 } 457 #ifndef SUPPRESS_WARNINGS 458 EXV_WARNING << "Unable to handle multiple PostScript pages. Found second page at position: " << startPos << "\n"; 459 #endif 460 throw Error(write ? 21 : 14); 461 } else if (line == "%%BeginPageSetup") { 462 posBeginPageSetup = startPos; 463 } else if (!inRemovableEmbedding && line == "%Exiv2BeginXMP: Before %%EndPageSetup") { 464 inRemovableEmbedding = true; 465 removableEmbeddings.push_back(std::make_pair(startPos, startPos)); 466 removableEmbeddingEndLine = "%Exiv2EndXMP"; 467 } else if (!inRemovableEmbedding && line == "%Exiv2BeginXMP: After %%PageTrailer") { 468 inRemovableEmbedding = true; 469 removableEmbeddings.push_back(std::make_pair(startPos, startPos)); 470 removableEmbeddingEndLine = "%Exiv2EndXMP"; 471 } else if (!inRemovableEmbedding && line == "%ADOBeginClientInjection: PageSetup End \"AI11EPS\"") { 472 inRemovableEmbedding = true; 473 removableEmbeddings.push_back(std::make_pair(startPos, startPos)); 474 removableEmbeddingEndLine = "%ADOEndClientInjection: PageSetup End \"AI11EPS\""; 475 } else if (!inRemovableEmbedding && line == "%ADOBeginClientInjection: PageTrailer Start \"AI11EPS\"") { 476 inRemovableEmbedding = true; 477 removableEmbeddings.push_back(std::make_pair(startPos, startPos)); 478 removableEmbeddingEndLine = "%ADOEndClientInjection: PageTrailer Start \"AI11EPS\""; 479 } else if (!inRemovableEmbedding && line == "%begin_xml_code") { 480 inRemovableEmbedding = true; 481 removableEmbeddings.push_back(std::make_pair(startPos, startPos)); 482 removableEmbeddingEndLine = "%end_xml_code"; 483 removableEmbeddingsWithUnmarkedTrailer++; 484 } else { 485 #ifdef DEBUG 486 significantLine = false; 487 #endif 488 } 489 #ifdef DEBUG 490 if (significantLine) { 491 EXV_DEBUG << "readWriteEpsMetadata: Found significant line \"" << line << "\" at position: " << startPos << "\n"; 492 } 493 significantLine = true; 494 #endif 495 // implicit comments 496 if (line == "%%EOF" || line == "%begin_xml_code" || !(line.size() >= 2 && line[0] == '%' && '\x21' <= line[1] && line[1] <= '\x7e')) { 497 if (posEndComments == posEndEps) { 498 posEndComments = startPos; 499 #ifdef DEBUG 500 EXV_DEBUG << "readWriteEpsMetadata: Found implicit EndComments at position: " << startPos << "\n"; 501 #endif 502 } 503 } 504 if (posPage == posEndEps && posEndComments != posEndEps && !inDefaultsPreviewPrologSetup && !inRemovableEmbedding && !onlyWhitespaces(line)) { 505 posPage = startPos; 506 implicitPage = true; 507 #ifdef DEBUG 508 EXV_DEBUG << "readWriteEpsMetadata: Found implicit Page at position: " << startPos << "\n"; 509 #endif 510 } 511 if (posBeginPageSetup == posEndEps && (implicitPage || (posPage != posEndEps && !inRemovableEmbedding && line.size() >= 1 && line[0] != '%'))) { 512 posBeginPageSetup = startPos; 513 implicitPageSetup = true; 514 #ifdef DEBUG 515 EXV_DEBUG << "readWriteEpsMetadata: Found implicit BeginPageSetup at position: " << startPos << "\n"; 516 #endif 517 } 518 if (posEndPageSetup == posEndEps && implicitPageSetup && !inRemovableEmbedding && line.size() >= 1 && line[0] != '%') { 519 posEndPageSetup = startPos; 520 #ifdef DEBUG 521 EXV_DEBUG << "readWriteEpsMetadata: Found implicit EndPageSetup at position: " << startPos << "\n"; 522 #endif 523 } 524 if (line.size() >= 1 && line[0] != '%') continue; // performance optimization 525 if (line == "%%EOF" || line == "%%Trailer" || line == "%%PageTrailer") { 526 if (posBeginPageSetup == posEndEps) { 527 posBeginPageSetup = startPos; 528 implicitPageSetup = true; 529 #ifdef DEBUG 530 EXV_DEBUG << "readWriteEpsMetadata: Found implicit BeginPageSetup at position: " << startPos << "\n"; 531 #endif 532 } 533 if (posEndPageSetup == posEndEps) { 534 posEndPageSetup = startPos; 535 implicitPageSetup = true; 536 #ifdef DEBUG 537 EXV_DEBUG << "readWriteEpsMetadata: Found implicit EndPageSetup at position: " << startPos << "\n"; 538 #endif 539 } 540 } 541 if (line == "%%EOF" || line == "%%Trailer") { 542 if (posPageTrailer == posEndEps) { 543 posPageTrailer = startPos; 544 implicitPageTrailer = true; 545 #ifdef DEBUG 546 EXV_DEBUG << "readWriteEpsMetadata: Found implicit PageTrailer at position: " << startPos << "\n"; 547 #endif 548 } 549 } 550 // remaining explicit comments 551 if (posEndComments == posEndEps && posLanguageLevel == posEndEps && startsWith(line, "%%LanguageLevel:")) { 552 posLanguageLevel = startPos; 553 } else if (posEndComments == posEndEps && posContainsXmp == posEndEps && startsWith(line, "%ADO_ContainsXMP:")) { 554 posContainsXmp = startPos; 555 } else if (posEndComments == posEndEps && posPages == posEndEps && startsWith(line, "%%Pages:")) { 556 posPages = startPos; 557 } else if (posEndComments == posEndEps && posExiv2Version == posEndEps && startsWith(line, "%Exiv2Version:")) { 558 posExiv2Version = startPos; 559 } else if (posEndComments == posEndEps && posExiv2Website == posEndEps && startsWith(line, "%Exiv2Website:")) { 560 posExiv2Website = startPos; 561 } else if (posEndComments == posEndEps && startsWith(line, "%%Creator: Adobe Illustrator") && firstLine == "%!PS-Adobe-3.0 EPSF-3.0") { 562 illustrator8 = true; 563 } else if (posEndComments == posEndEps && startsWith(line, "%AI7_Thumbnail:")) { 564 posAi7Thumbnail = startPos; 565 } else if (posEndComments == posEndEps && posAi7Thumbnail != posEndEps && posAi7ThumbnailEndData == posEndEps && line == "%%EndData") { 566 posAi7ThumbnailEndData = startPos; 567 } else if (posEndComments == posEndEps && line == "%%EndComments") { 568 posEndComments = startPos; 569 } else if (inDefaultsPreviewPrologSetup && startsWith(line, "%%BeginResource: procset wCorel")) { 570 corelDraw = true; 571 } else if (line == "%%EndPreview") { 572 inDefaultsPreviewPrologSetup = false; 573 } else if (line == "%%EndDefaults") { 574 inDefaultsPreviewPrologSetup = false; 575 } else if (line == "%%EndProlog") { 576 inDefaultsPreviewPrologSetup = false; 577 } else if (line == "%%EndSetup") { 578 inDefaultsPreviewPrologSetup = false; 579 } else if (posEndPageSetup == posEndEps && line == "%%EndPageSetup") { 580 posEndPageSetup = startPos; 581 } else if (posPageTrailer == posEndEps && line == "%%PageTrailer") { 582 posPageTrailer = startPos; 583 } else if (posBeginPhotoshop == posEndEps && startsWith(line, "%BeginPhotoshop:")) { 584 posBeginPhotoshop = pos; 585 } else if (posBeginPhotoshop != posEndEps && posEndPhotoshop == posEndEps && line == "%EndPhotoshop") { 586 posEndPhotoshop = startPos; 587 } else if (inRemovableEmbedding && line == removableEmbeddingEndLine) { 588 inRemovableEmbedding = false; 589 removableEmbeddings.back().second = pos; 590 } else if (line == "%%EOF") { 591 posEof = startPos; 592 } else { 593 #ifdef DEBUG 594 significantLine = false; 595 #endif 596 } 597 #ifdef DEBUG 598 if (significantLine) { 599 EXV_DEBUG << "readWriteEpsMetadata: Found significant line \"" << line << "\" at position: " << startPos << "\n"; 600 } 601 #endif 602 } 603 604 // check for unfinished nested documents 605 if (depth != 0) { 606 #ifndef SUPPRESS_WARNINGS 607 EXV_WARNING << "Unmatched BeginDocument (" << depth << "x)\n"; 608 #endif 609 throw Error(write ? 21 : 14); 610 } 611 612 // look for the unmarked trailers of some removable XMP embeddings 613 size_t posXmpTrailerEnd = posEof; 614 for (size_t i = 0; i < removableEmbeddingsWithUnmarkedTrailer; i++) { 615 std::string line1; 616 const size_t posLine1 = readPrevLine(line1, data, posXmpTrailerEnd, posEndEps); 617 std::string line2; 618 const size_t posLine2 = readPrevLine(line2, data, posLine1, posEndEps); 619 size_t posXmpTrailer; 620 if (line1 == "[/EMC pdfmark") { // Exiftool style 621 posXmpTrailer = posLine1; 622 } else if (line1 == "[/NamespacePop pdfmark" && 623 line2 == "[{nextImage} 1 dict begin /Metadata {photoshop_metadata_stream} def currentdict end /PUT pdfmark") { // Photoshop style 624 posXmpTrailer = posLine2; 625 } else { 626 #ifndef SUPPRESS_WARNINGS 627 EXV_WARNING << "Unable to find XMP embedding trailer ending at position: " << posXmpTrailerEnd << "\n"; 628 #endif 629 if (write) throw Error(21); 630 break; 631 } 632 removableEmbeddings.push_back(std::make_pair(posXmpTrailer, posXmpTrailerEnd)); 633 #ifdef DEBUG 634 EXV_DEBUG << "readWriteEpsMetadata: Recognized unmarked trailer of removable XMP embedding at " 635 "[" << removableEmbeddings.back().first << "," << removableEmbeddings.back().second << ")" 636 "\n"; 637 #endif 638 posXmpTrailerEnd = posXmpTrailer; 639 } 640 641 // interpret comment "%ADO_ContainsXMP:" 642 std::string line; 643 readLine(line, data, posContainsXmp, posEndEps); 644 bool containsXmp; 645 if (line == "%ADO_ContainsXMP: MainFirst" || line == "%ADO_ContainsXMP:MainFirst") { 646 containsXmp = true; 647 } else if (line == "" || line == "%ADO_ContainsXMP: NoMain" || line == "%ADO_ContainsXMP:NoMain") { 648 containsXmp = false; 649 } else { 650 #ifndef SUPPRESS_WARNINGS 651 EXV_WARNING << "Invalid line \"" << line << "\" at position: " << posContainsXmp << "\n"; 652 #endif 653 throw Error(write ? 21 : 14); 654 } 655 656 const bool deleteXmp = (write && xmpPacket.size() == 0); 657 bool fixBeginXmlPacket = false; 658 bool useFlexibleEmbedding = false; 659 size_t xmpPos = posEndEps; 660 size_t xmpSize = 0; 661 if (containsXmp) { 662 // search for XMP metadata 663 findXmp(xmpPos, xmpSize, data, posEps, posEndEps, write); 664 if (xmpPos == posEndEps) { 665 #ifndef SUPPRESS_WARNINGS 666 EXV_WARNING << "Unable to find XMP metadata as announced at position: " << posContainsXmp << "\n"; 667 #endif 668 } 669 // check embedding of XMP metadata 670 const size_t posLineAfterXmp = readLine(line, data, xmpPos + xmpSize, posEndEps); 671 if (line != "") { 672 #ifndef SUPPRESS_WARNINGS 673 EXV_WARNING << "Unexpected " << line.size() << " bytes of data after XMP at position: " << (xmpPos + xmpSize) << "\n"; 674 #endif 675 } else if (!deleteXmp) { 676 readLine(line, data, posLineAfterXmp, posEndEps); 677 if (line == "% &&end XMP packet marker&&" || line == "% &&end XMP packet marker&&") { 678 useFlexibleEmbedding = true; 679 } 680 } 681 } 682 if (useFlexibleEmbedding) { 683 #ifdef DEBUG 684 EXV_DEBUG << "readWriteEpsMetadata: Using flexible XMP embedding\n"; 685 #endif 686 const size_t posBeginXmlPacket = readPrevLine(line, data, xmpPos, posEndEps); 687 if (startsWith(line, "%begin_xml_packet:")) { 688 #ifdef DEBUG 689 EXV_DEBUG << "readWriteEpsMetadata: XMP embedding contains %begin_xml_packet\n"; 690 #endif 691 if (write) { 692 fixBeginXmlPacket = true; 693 xmpSize += (xmpPos - posBeginXmlPacket); 694 xmpPos = posBeginXmlPacket; 695 } 696 } else if (posBeginPhotoshop != posEndEps) { 697 #ifndef SUPPRESS_WARNINGS 698 EXV_WARNING << "Missing %begin_xml_packet in Photoshop EPS at position: " << xmpPos << "\n"; 699 #endif 700 if (write) throw Error(21); 701 } 702 } 703 if (!useFlexibleEmbedding) { 704 // check if there are irremovable XMP metadata blocks before EndPageSetup 705 size_t posOtherXmp = containsXmp ? xmpPos : posEps; 706 size_t sizeOtherXmp = 0; 707 for (;;) { 708 findXmp(posOtherXmp, sizeOtherXmp, data, posOtherXmp + sizeOtherXmp, posEndPageSetup, write); 709 if (posOtherXmp >= posEndPageSetup) break; 710 bool isRemovableEmbedding = false; 711 for (std::vector<std::pair<size_t, size_t> >::const_iterator e = removableEmbeddings.begin(); e != removableEmbeddings.end(); e++) { 712 if (e->first <= posOtherXmp && posOtherXmp < e->second) { 713 isRemovableEmbedding = true; 714 break; 715 } 716 } 717 if (!isRemovableEmbedding) { 718 #ifndef SUPPRESS_WARNINGS 719 EXV_WARNING << "XMP metadata block is not removable at position: " << posOtherXmp << "\n"; 720 #endif 721 if (write) throw Error(21); 722 break; 723 } 724 } 725 } 726 727 if (!write) { 728 // copy XMP metadata 729 xmpPacket.assign(reinterpret_cast<const char*>(data + xmpPos), xmpSize); 730 731 // native previews 732 nativePreviews.clear(); 733 if (posAi7ThumbnailEndData != posEndEps) { 734 NativePreview nativePreview; 735 std::string dummy; 736 std::string lineAi7Thumbnail; 737 const size_t posBeginData = readLine(lineAi7Thumbnail, data, posAi7Thumbnail, posEndEps); 738 std::istringstream lineStreamAi7Thumbnail(lineAi7Thumbnail); 739 lineStreamAi7Thumbnail >> dummy; 740 lineStreamAi7Thumbnail >> nativePreview.width_; 741 lineStreamAi7Thumbnail >> nativePreview.height_; 742 std::string depth; 743 lineStreamAi7Thumbnail >> depth; 744 std::string lineBeginData; 745 const size_t posAfterBeginData = readLine(lineBeginData, data, posBeginData, posEndEps); 746 std::istringstream lineStreamBeginData(lineBeginData); 747 std::string beginData; 748 lineStreamBeginData >> beginData; 749 lineStreamBeginData >> dummy; 750 std::string type; 751 lineStreamBeginData >> type; 752 nativePreview.position_ = static_cast<long>(posAfterBeginData); 753 nativePreview.size_ = static_cast<uint32_t>(posAi7ThumbnailEndData - posAfterBeginData); 754 nativePreview.filter_ = "hex-ai7thumbnail-pnm"; 755 nativePreview.mimeType_ = "image/x-portable-anymap"; 756 if (depth != "8") { 757 #ifndef SUPPRESS_WARNINGS 758 EXV_WARNING << "Unable to handle Illustrator thumbnail depth: " << depth << "\n"; 759 #endif 760 } else if (beginData != "%%BeginData:") { 761 #ifndef SUPPRESS_WARNINGS 762 EXV_WARNING << "Unable to handle Illustrator thumbnail data section: " << lineBeginData << "\n"; 763 #endif 764 } else if (type != "Hex") { 765 #ifndef SUPPRESS_WARNINGS 766 EXV_WARNING << "Unable to handle Illustrator thumbnail data type: " << type << "\n"; 767 #endif 768 } else { 769 nativePreviews.push_back(nativePreview); 770 } 771 } 772 if (posEndPhotoshop != posEndEps) { 773 NativePreview nativePreview; 774 nativePreview.position_ = static_cast<long>(posBeginPhotoshop); 775 nativePreview.size_ = static_cast<uint32_t>(posEndPhotoshop - posBeginPhotoshop); 776 nativePreview.width_ = 0; 777 nativePreview.height_ = 0; 778 nativePreview.filter_ = "hex-irb"; 779 nativePreview.mimeType_ = "image/jpeg"; 780 nativePreviews.push_back(nativePreview); 781 } 782 if (sizeWmf != 0) { 783 NativePreview nativePreview; 784 nativePreview.position_ = static_cast<long>(posWmf); 785 nativePreview.size_ = sizeWmf; 786 nativePreview.width_ = 0; 787 nativePreview.height_ = 0; 788 nativePreview.filter_ = ""; 789 nativePreview.mimeType_ = "image/x-wmf"; 790 nativePreviews.push_back(nativePreview); 791 } 792 if (sizeTiff != 0) { 793 NativePreview nativePreview; 794 nativePreview.position_ = static_cast<long>(posTiff); 795 nativePreview.size_ = sizeTiff; 796 nativePreview.width_ = 0; 797 nativePreview.height_ = 0; 798 nativePreview.filter_ = ""; 799 nativePreview.mimeType_ = "image/tiff"; 800 nativePreviews.push_back(nativePreview); 801 } 802 } else { 803 // check for Adobe Illustrator 8.0 or older 804 if (illustrator8) { 805 #ifndef SUPPRESS_WARNINGS 806 EXV_WARNING << "Unable to write to EPS files created by Adobe Illustrator 8.0 or older.\n"; 807 #endif 808 throw Error(21); 809 } 810 811 // create temporary output file 812 BasicIo::AutoPtr tempIo(new MemIo); 813 assert (tempIo.get() != 0); 814 if (!tempIo->isopen()) { 815 #ifndef SUPPRESS_WARNINGS 816 EXV_WARNING << "Unable to create temporary file for writing.\n"; 817 #endif 818 throw Error(21); 819 } 820 #ifdef DEBUG 821 EXV_DEBUG << "readWriteEpsMetadata: Created temporary file " << tempIo->path() << "\n"; 822 #endif 823 824 // sort all positions 825 std::vector<size_t> positions; 826 positions.push_back(posLanguageLevel); 827 positions.push_back(posContainsXmp); 828 positions.push_back(posPages); 829 positions.push_back(posExiv2Version); 830 positions.push_back(posExiv2Website); 831 positions.push_back(posEndComments); 832 positions.push_back(posPage); 833 positions.push_back(posBeginPageSetup); 834 positions.push_back(posEndPageSetup); 835 positions.push_back(posPageTrailer); 836 positions.push_back(posEof); 837 positions.push_back(posEndEps); 838 if (useFlexibleEmbedding) { 839 positions.push_back(xmpPos); 840 } 841 for (std::vector<std::pair<size_t, size_t> >::const_iterator e = removableEmbeddings.begin(); e != removableEmbeddings.end(); e++) { 842 positions.push_back(e->first); 843 } 844 std::sort(positions.begin(), positions.end()); 845 846 // assemble result EPS document 847 if (dosEps) { 848 // DOS EPS header will be written afterwards 849 writeTemp(*tempIo, std::string(30, '\x00')); 850 } 851 const std::string containsXmpLine = deleteXmp ? "%ADO_ContainsXMP: NoMain" : "%ADO_ContainsXMP: MainFirst"; 852 const uint32_t posEpsNew = posTemp(*tempIo); 853 size_t prevPos = posEps; 854 size_t prevSkipPos = prevPos; 855 for (std::vector<size_t>::const_iterator i = positions.begin(); i != positions.end(); i++) { 856 const size_t pos = *i; 857 if (pos == prevPos) continue; 858 #ifdef DEBUG 859 EXV_DEBUG << "readWriteEpsMetadata: Writing at " << pos << "\n"; 860 #endif 861 if (pos < prevSkipPos) { 862 #ifndef SUPPRESS_WARNINGS 863 EXV_WARNING << "Internal error while assembling the result EPS document: " 864 "Unable to continue at position " << pos << " after skipping to position " << prevSkipPos << "\n"; 865 #endif 866 throw Error(21); 867 } 868 writeTemp(*tempIo, data + prevSkipPos, pos - prevSkipPos); 869 const size_t posLineEnd = readLine(line, data, pos, posEndEps); 870 size_t skipPos = pos; 871 // add last line ending if necessary 872 if (pos == posEndEps && pos >= 1 && data[pos - 1] != '\r' && data[pos - 1] != '\n') { 873 writeTemp(*tempIo, lineEnding); 874 #ifdef DEBUG 875 EXV_DEBUG << "readWriteEpsMetadata: Added missing line ending of last line\n"; 876 #endif 877 } 878 // update and complement DSC comments 879 if (pos == posLanguageLevel && posLanguageLevel != posEndEps && !deleteXmp && !useFlexibleEmbedding) { 880 if (line == "%%LanguageLevel:1" || line == "%%LanguageLevel: 1") { 881 writeTemp(*tempIo, "%%LanguageLevel: 2" + lineEnding); 882 skipPos = posLineEnd; 883 #ifdef DEBUG 884 EXV_DEBUG << "readWriteEpsMetadata: Skipping to " << skipPos << " at " << __FILE__ << ":" << __LINE__ << "\n"; 885 #endif 886 } 887 } 888 if (pos == posContainsXmp && posContainsXmp != posEndEps) { 889 if (line != containsXmpLine) { 890 writeTemp(*tempIo, containsXmpLine + lineEnding); 891 skipPos = posLineEnd; 892 #ifdef DEBUG 893 EXV_DEBUG << "readWriteEpsMetadata: Skipping to " << skipPos << " at " << __FILE__ << ":" << __LINE__ << "\n"; 894 #endif 895 } 896 } 897 if (pos == posExiv2Version && posExiv2Version != posEndEps) { 898 writeTemp(*tempIo, "%Exiv2Version: " + versionNumberHexString() + lineEnding); 899 skipPos = posLineEnd; 900 #ifdef DEBUG 901 EXV_DEBUG << "readWriteEpsMetadata: Skipping to " << skipPos << " at " << __FILE__ << ":" << __LINE__ << "\n"; 902 #endif 903 } 904 if (pos == posExiv2Website && posExiv2Website != posEndEps) { 905 writeTemp(*tempIo, "%Exiv2Website: http://www.exiv2.org/" + lineEnding); 906 skipPos = posLineEnd; 907 #ifdef DEBUG 908 EXV_DEBUG << "readWriteEpsMetadata: Skipping to " << skipPos << " at " << __FILE__ << ":" << __LINE__ << "\n"; 909 #endif 910 } 911 if (pos == posEndComments) { 912 if (posLanguageLevel == posEndEps && !deleteXmp && !useFlexibleEmbedding) { 913 writeTemp(*tempIo, "%%LanguageLevel: 2" + lineEnding); 914 } 915 if (posContainsXmp == posEndEps) { 916 writeTemp(*tempIo, containsXmpLine + lineEnding); 917 } 918 if (posPages == posEndEps) { 919 writeTemp(*tempIo, "%%Pages: 1" + lineEnding); 920 } 921 if (posExiv2Version == posEndEps) { 922 writeTemp(*tempIo, "%Exiv2Version: " + versionNumberHexString() + lineEnding); 923 } 924 if (posExiv2Website == posEndEps) { 925 writeTemp(*tempIo, "%Exiv2Website: http://www.exiv2.org/" + lineEnding); 926 } 927 readLine(line, data, posEndComments, posEndEps); 928 if (line != "%%EndComments") { 929 writeTemp(*tempIo, "%%EndComments" + lineEnding); 930 } 931 } 932 if (pos == posPage) { 933 if (!startsWith(line, "%%Page:")) { 934 writeTemp(*tempIo, "%%Page: 1 1" + lineEnding); 935 writeTemp(*tempIo, "%%EndPageComments" + lineEnding); 936 } 937 } 938 if (pos == posBeginPageSetup) { 939 if (line != "%%BeginPageSetup") { 940 writeTemp(*tempIo, "%%BeginPageSetup" + lineEnding); 941 } 942 } 943 if (useFlexibleEmbedding) { 944 // insert XMP metadata into existing flexible embedding 945 if (pos == xmpPos) { 946 if (fixBeginXmlPacket) { 947 writeTemp(*tempIo, "%begin_xml_packet: " + toString(xmpPacket.size()) + lineEnding); 948 } 949 writeTemp(*tempIo, xmpPacket); 950 skipPos += xmpSize; 951 #ifdef DEBUG 952 EXV_DEBUG << "readWriteEpsMetadata: Skipping to " << skipPos << " at " << __FILE__ << ":" << __LINE__ << "\n"; 953 #endif 954 } 955 } 956 if (!useFlexibleEmbedding) { 957 // remove preceding embedding(s) 958 for (std::vector<std::pair<size_t, size_t> >::const_iterator e = removableEmbeddings.begin(); e != removableEmbeddings.end(); e++) { 959 if (pos == e->first) { 960 skipPos = e->second; 961 #ifdef DEBUG 962 EXV_DEBUG << "readWriteEpsMetadata: Skipping to " << skipPos << " at " << __FILE__ << ":" << __LINE__ << "\n"; 963 #endif 964 break; 965 } 966 } 967 // insert XMP metadata with new flexible embedding, if necessary 968 if (pos == posEndPageSetup && !deleteXmp) { 969 writeTemp(*tempIo, "%Exiv2BeginXMP: Before %%EndPageSetup" + lineEnding); 970 if (corelDraw) { 971 writeTemp(*tempIo, "%Exiv2Notice: The following line is needed by CorelDRAW." + lineEnding); 972 writeTemp(*tempIo, "@rs" + lineEnding); 973 } 974 if (posBeginPhotoshop != posEndEps) { 975 writeTemp(*tempIo, "%Exiv2Notice: The following line is needed by Photoshop." + lineEnding); 976 writeTemp(*tempIo, "%begin_xml_code" + lineEnding); 977 } 978 writeTemp(*tempIo, "/currentdistillerparams where" + lineEnding); 979 writeTemp(*tempIo, "{pop currentdistillerparams /CoreDistVersion get 5000 lt} {true} ifelse" + lineEnding); 980 writeTemp(*tempIo, "{userdict /Exiv2_pdfmark /cleartomark load put" + lineEnding); 981 writeTemp(*tempIo, " userdict /Exiv2_metafile_pdfmark {flushfile cleartomark} bind put}" + lineEnding); 982 writeTemp(*tempIo, "{userdict /Exiv2_pdfmark /pdfmark load put" + lineEnding); 983 writeTemp(*tempIo, " userdict /Exiv2_metafile_pdfmark {/PUT pdfmark} bind put} ifelse" + lineEnding); 984 writeTemp(*tempIo, "[/NamespacePush Exiv2_pdfmark" + lineEnding); 985 writeTemp(*tempIo, "[/_objdef {Exiv2_metadata_stream} /type /stream /OBJ Exiv2_pdfmark" + lineEnding); 986 writeTemp(*tempIo, "[{Exiv2_metadata_stream} 2 dict begin" + lineEnding); 987 writeTemp(*tempIo, " /Type /Metadata def /Subtype /XML def currentdict end /PUT Exiv2_pdfmark" + lineEnding); 988 writeTemp(*tempIo, "[{Exiv2_metadata_stream}" + lineEnding); 989 writeTemp(*tempIo, " currentfile 0 (% &&end XMP packet marker&&)" + lineEnding); 990 writeTemp(*tempIo, " /SubFileDecode filter Exiv2_metafile_pdfmark" + lineEnding); 991 if (posBeginPhotoshop != posEndEps) { 992 writeTemp(*tempIo, "%Exiv2Notice: The following line is needed by Photoshop. " 993 "Parameter must be exact size of XMP metadata." + lineEnding); 994 writeTemp(*tempIo, "%begin_xml_packet: " + toString(xmpPacket.size()) + lineEnding); 995 } 996 writeTemp(*tempIo, xmpPacket); 997 writeTemp(*tempIo, lineEnding); 998 writeTemp(*tempIo, "% &&end XMP packet marker&&" + lineEnding); 999 writeTemp(*tempIo, "[/Document 1 dict begin" + lineEnding); 1000 writeTemp(*tempIo, " /Metadata {Exiv2_metadata_stream} def currentdict end /BDC Exiv2_pdfmark" + lineEnding); 1001 if (posBeginPhotoshop != posEndEps) { 1002 writeTemp(*tempIo, "%Exiv2Notice: The following line is needed by Photoshop." + lineEnding); 1003 writeTemp(*tempIo, "%end_xml_code" + lineEnding); 1004 } 1005 if (corelDraw) { 1006 writeTemp(*tempIo, "%Exiv2Notice: The following line is needed by CorelDRAW." + lineEnding); 1007 writeTemp(*tempIo, "@sv" + lineEnding); 1008 } 1009 writeTemp(*tempIo, "%Exiv2EndXMP" + lineEnding); 1010 } 1011 } 1012 if (pos == posEndPageSetup) { 1013 if (line != "%%EndPageSetup") { 1014 writeTemp(*tempIo, "%%EndPageSetup" + lineEnding); 1015 } 1016 } 1017 if (!useFlexibleEmbedding) { 1018 if (pos == posPageTrailer && !deleteXmp) { 1019 if (!implicitPageTrailer) { 1020 skipPos = posLineEnd; 1021 #ifdef DEBUG 1022 EXV_DEBUG << "readWriteEpsMetadata: Skipping to " << skipPos << " at " << __FILE__ << ":" << __LINE__ << "\n"; 1023 #endif 1024 } 1025 writeTemp(*tempIo, "%%PageTrailer" + lineEnding); 1026 writeTemp(*tempIo, "%Exiv2BeginXMP: After %%PageTrailer" + lineEnding); 1027 writeTemp(*tempIo, "[/EMC Exiv2_pdfmark" + lineEnding); 1028 writeTemp(*tempIo, "[/NamespacePop Exiv2_pdfmark" + lineEnding); 1029 writeTemp(*tempIo, "%Exiv2EndXMP" + lineEnding); 1030 } 1031 } 1032 // add EOF comment if necessary 1033 if (pos == posEndEps && posEof == posEndEps) { 1034 writeTemp(*tempIo, "%%EOF" + lineEnding); 1035 } 1036 prevPos = pos; 1037 prevSkipPos = skipPos; 1038 } 1039 const uint32_t posEndEpsNew = posTemp(*tempIo); 1040 #ifdef DEBUG 1041 EXV_DEBUG << "readWriteEpsMetadata: New EPS size: " << (posEndEpsNew - posEpsNew) << "\n"; 1042 #endif 1043 if (dosEps) { 1044 // write WMF and/or TIFF section if present 1045 writeTemp(*tempIo, data + posWmf, sizeWmf); 1046 writeTemp(*tempIo, data + posTiff, sizeTiff); 1047 #ifdef DEBUG 1048 EXV_DEBUG << "readWriteEpsMetadata: New DOS EPS total size: " << posTemp(*tempIo) << "\n"; 1049 #endif 1050 // write DOS EPS header 1051 if (tempIo->seek(0, BasicIo::beg) != 0) { 1052 #ifndef SUPPRESS_WARNINGS 1053 EXV_WARNING << "Internal error while seeking in temporary file.\n"; 1054 #endif 1055 throw Error(21); 1056 } 1057 byte dosEpsHeader[30]; 1058 dosEpsSignature.copy(reinterpret_cast<char*>(dosEpsHeader), dosEpsSignature.size()); 1059 ul2Data(dosEpsHeader + 4, posEpsNew, littleEndian); 1060 ul2Data(dosEpsHeader + 8, posEndEpsNew - posEpsNew, littleEndian); 1061 ul2Data(dosEpsHeader + 12, sizeWmf == 0 ? 0 : posEndEpsNew, littleEndian); 1062 ul2Data(dosEpsHeader + 16, sizeWmf, littleEndian); 1063 ul2Data(dosEpsHeader + 20, sizeTiff == 0 ? 0 : posEndEpsNew + sizeWmf, littleEndian); 1064 ul2Data(dosEpsHeader + 24, sizeTiff, littleEndian); 1065 us2Data(dosEpsHeader + 28, 0xFFFF, littleEndian); 1066 writeTemp(*tempIo, dosEpsHeader, sizeof(dosEpsHeader)); 1067 } 1068 1069 // copy temporary file to real output file 1070 io.close(); 1071 io.transfer(*tempIo); 1072 } 1073 } 1074 1075 } // namespace 1076 1077 // ***************************************************************************** 1078 // class member definitions 1079 namespace Exiv2 1080 { 1081 EpsImage(BasicIo::AutoPtr io,bool create)1082 EpsImage::EpsImage(BasicIo::AutoPtr io, bool create) 1083 : Image(ImageType::eps, mdXmp, io) 1084 { 1085 //LogMsg::setLevel(LogMsg::debug); 1086 if (create) { 1087 if (io_->open() == 0) { 1088 #ifdef DEBUG 1089 EXV_DEBUG << "Exiv2::EpsImage:: Creating blank EPS image\n"; 1090 #endif 1091 IoCloser closer(*io_); 1092 if (io_->write(reinterpret_cast<const byte*>(epsBlank.data()), static_cast<long>(epsBlank.size())) != static_cast<long>(epsBlank.size())) { 1093 #ifndef SUPPRESS_WARNINGS 1094 EXV_WARNING << "Failed to write blank EPS image.\n"; 1095 #endif 1096 throw Error(21); 1097 } 1098 } 1099 } 1100 } 1101 mimeType() const1102 std::string EpsImage::mimeType() const 1103 { 1104 return "application/postscript"; 1105 } 1106 setComment(const std::string &)1107 void EpsImage::setComment(const std::string& /*comment*/) 1108 { 1109 throw Error(32, "Image comment", "EPS"); 1110 } 1111 readMetadata()1112 void EpsImage::readMetadata() 1113 { 1114 #ifdef DEBUG 1115 EXV_DEBUG << "Exiv2::EpsImage::readMetadata: Reading EPS file " << io_->path() << "\n"; 1116 #endif 1117 1118 // read metadata 1119 readWriteEpsMetadata(*io_, xmpPacket_, nativePreviews_, /* write = */ false); 1120 1121 // decode XMP metadata 1122 if (xmpPacket_.size() > 0 && XmpParser::decode(xmpData_, xmpPacket_) > 1) { 1123 #ifndef SUPPRESS_WARNINGS 1124 EXV_WARNING << "Failed to decode XMP metadata.\n"; 1125 #endif 1126 throw Error(14); 1127 } 1128 1129 #ifdef DEBUG 1130 EXV_DEBUG << "Exiv2::EpsImage::readMetadata: Finished reading EPS file " << io_->path() << "\n"; 1131 #endif 1132 } 1133 writeMetadata()1134 void EpsImage::writeMetadata() 1135 { 1136 #ifdef DEBUG 1137 EXV_DEBUG << "Exiv2::EpsImage::writeMetadata: Writing EPS file " << io_->path() << "\n"; 1138 #endif 1139 1140 // encode XMP metadata if necessary 1141 if (!writeXmpFromPacket() && XmpParser::encode(xmpPacket_, xmpData_) > 1) { 1142 #ifndef SUPPRESS_WARNINGS 1143 EXV_WARNING << "Failed to encode XMP metadata.\n"; 1144 #endif 1145 throw Error(21); 1146 } 1147 1148 // write metadata 1149 readWriteEpsMetadata(*io_, xmpPacket_, nativePreviews_, /* write = */ true); 1150 1151 #ifdef DEBUG 1152 EXV_DEBUG << "Exiv2::EpsImage::writeMetadata: Finished writing EPS file " << io_->path() << "\n"; 1153 #endif 1154 } 1155 1156 // ************************************************************************* 1157 // free functions newEpsInstance(BasicIo::AutoPtr io,bool create)1158 Image::AutoPtr newEpsInstance(BasicIo::AutoPtr io, bool create) 1159 { 1160 Image::AutoPtr image(new EpsImage(io, create)); 1161 if (!image->good()) { 1162 image.reset(); 1163 } 1164 return image; 1165 } 1166 isEpsType(BasicIo & iIo,bool advance)1167 bool isEpsType(BasicIo& iIo, bool advance) 1168 { 1169 // read as many bytes as needed for the longest (DOS) EPS signature 1170 long bufSize = static_cast<long>(dosEpsSignature.size()); 1171 for (size_t i = 0; i < (sizeof epsFirstLine) / (sizeof *epsFirstLine); i++) { 1172 if (bufSize < static_cast<long>(epsFirstLine[i].size())) { 1173 bufSize = static_cast<long>(epsFirstLine[i].size()); 1174 } 1175 } 1176 DataBuf buf = iIo.read(bufSize); 1177 if (iIo.error() || buf.size_ != bufSize) { 1178 return false; 1179 } 1180 // check for all possible (DOS) EPS signatures 1181 bool matched = (memcmp(buf.pData_, dosEpsSignature.data(), dosEpsSignature.size()) == 0); 1182 for (size_t i = 0; !matched && i < (sizeof epsFirstLine) / (sizeof *epsFirstLine); i++) { 1183 matched = (memcmp(buf.pData_, epsFirstLine[i].data(), epsFirstLine[i].size()) == 0); 1184 } 1185 // seek back if possible and requested 1186 if (!advance || !matched) { 1187 iIo.seek(-buf.size_, BasicIo::cur); 1188 } 1189 return matched; 1190 } 1191 1192 } // namespace Exiv2 1193