|
Packit |
01d647 |
// ***************************************************************** -*- C++ -*-
|
|
Packit |
01d647 |
/*
|
|
Packit |
01d647 |
* Copyright (C) 2004-2018 Exiv2 authors
|
|
Packit |
01d647 |
* This program is part of the Exiv2 distribution.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This program is free software; you can redistribute it and/or
|
|
Packit |
01d647 |
* modify it under the terms of the GNU General Public License
|
|
Packit |
01d647 |
* as published by the Free Software Foundation; either version 2
|
|
Packit |
01d647 |
* of the License, or (at your option) any later version.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
01d647 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
01d647 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
01d647 |
* GNU General Public License for more details.
|
|
Packit |
01d647 |
*
|
|
Packit |
01d647 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
01d647 |
* along with this program; if not, write to the Free Software
|
|
Packit |
01d647 |
* Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
/*
|
|
Packit |
01d647 |
File: epsimage.cpp
|
|
Packit |
01d647 |
Author(s): Michael Ulbrich (mul) <mul@rentapacs.de>
|
|
Packit |
01d647 |
Volker Grabsch (vog) <vog@notjusthosting.com>
|
|
Packit |
01d647 |
History: 7-Mar-2011, vog: created
|
|
Packit |
01d647 |
*/
|
|
Packit |
01d647 |
// *****************************************************************************
|
|
Packit |
01d647 |
// included header files
|
|
Packit |
01d647 |
#include "config.h"
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#include "epsimage.hpp"
|
|
Packit |
01d647 |
#include "image.hpp"
|
|
Packit |
01d647 |
#include "basicio.hpp"
|
|
Packit |
01d647 |
#include "error.hpp"
|
|
Packit |
01d647 |
#include "futils.hpp"
|
|
Packit |
01d647 |
#include "version.hpp"
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// + standard includes
|
|
Packit |
01d647 |
#include <algorithm>
|
|
Packit |
01d647 |
#include <cassert>
|
|
Packit |
01d647 |
#include <climits>
|
|
Packit |
01d647 |
#include <cstring>
|
|
Packit |
01d647 |
#include <iostream>
|
|
Packit |
01d647 |
#include <sstream>
|
|
Packit |
01d647 |
#include <string>
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// *****************************************************************************
|
|
Packit |
01d647 |
namespace {
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
using namespace Exiv2;
|
|
Packit |
01d647 |
using Exiv2::byte;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// signature of DOS EPS
|
|
Packit |
01d647 |
const std::string dosEpsSignature = "\xC5\xD0\xD3\xC6";
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// first line of EPS
|
|
Packit |
01d647 |
const std::string epsFirstLine[] = {
|
|
Packit |
01d647 |
"%!PS-Adobe-3.0 EPSF-3.0",
|
|
Packit |
01d647 |
"%!PS-Adobe-3.0 EPSF-3.0 ", // OpenOffice
|
|
Packit |
01d647 |
"%!PS-Adobe-3.1 EPSF-3.0", // Illustrator
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// blank EPS file
|
|
Packit |
01d647 |
const std::string epsBlank = "%!PS-Adobe-3.0 EPSF-3.0\n"
|
|
Packit |
01d647 |
"%%BoundingBox: 0 0 0 0\n";
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// list of all valid XMP headers
|
|
Packit |
01d647 |
const std::string xmpHeaders[] = {
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// We do not enforce the trailing "?>" here, because the XMP specification
|
|
Packit |
01d647 |
// permits additional attributes after begin="..." and id="...".
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// normal headers
|
|
Packit |
01d647 |
"
|
|
Packit |
01d647 |
"
|
|
Packit |
01d647 |
"
|
|
Packit |
01d647 |
"
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// deprecated headers (empty begin attribute, UTF-8 only)
|
|
Packit |
01d647 |
"
|
|
Packit |
01d647 |
"
|
|
Packit |
01d647 |
"
|
|
Packit |
01d647 |
"
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// list of all valid XMP trailers
|
|
Packit |
01d647 |
struct XmpTrailer {
|
|
Packit |
01d647 |
std::string trailer;
|
|
Packit |
01d647 |
bool readOnly;
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
const XmpTrailer xmpTrailers[] = {
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// We do not enforce the trailing "?>" here, because the XMP specification
|
|
Packit |
01d647 |
// permits additional attributes after end="...".
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
{"
|
|
Packit |
01d647 |
{"
|
|
Packit |
01d647 |
{"
|
|
Packit |
01d647 |
{"
|
|
Packit |
01d647 |
};
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// closing part of all valid XMP trailers
|
|
Packit |
01d647 |
const std::string xmpTrailerEnd = "?>";
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
//! Write data into temp file, taking care of errors
|
|
Packit |
01d647 |
void writeTemp(BasicIo& tempIo, const byte* data, size_t size)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
if (size == 0) return;
|
|
Packit |
01d647 |
if (tempIo.write(data, static_cast<long>(size)) != static_cast<long>(size)) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Failed to write to temporary file.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
//! Write data into temp file, taking care of errors
|
|
Packit |
01d647 |
void writeTemp(BasicIo& tempIo, const std::string &data)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
writeTemp(tempIo, reinterpret_cast<const byte*>(data.data()), data.size());
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
//! Get the current write position of temp file, taking care of errors
|
|
Packit |
01d647 |
uint32_t posTemp(BasicIo& tempIo)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
const long pos = tempIo.tell();
|
|
Packit |
01d647 |
if (pos == -1) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Internal error while determining current write position in temporary file.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
return static_cast<uint32_t>(pos);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
//! Check whether a string has a certain beginning
|
|
Packit |
01d647 |
bool startsWith(const std::string& s, const std::string& start)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
return s.size() >= start.size() && memcmp(s.data(), start.data(), start.size()) == 0;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
//! Check whether a string contains only white space characters
|
|
Packit |
01d647 |
bool onlyWhitespaces(const std::string& s)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
// According to the DSC 3.0 specification, 4.4 Parsing Rules,
|
|
Packit |
01d647 |
// only spaces and tabs are considered to be white space characters.
|
|
Packit |
01d647 |
return s.find_first_not_of(" \t") == std::string::npos;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
//! Read the next line of a buffer, allow for changing line ending style
|
|
Packit |
01d647 |
size_t readLine(std::string& line, const byte* data, size_t startPos, size_t size)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
line.clear();
|
|
Packit |
01d647 |
size_t pos = startPos;
|
|
Packit |
01d647 |
// step through line
|
|
Packit |
01d647 |
while (pos < size && data[pos] != '\r' && data[pos] != '\n') {
|
|
Packit |
01d647 |
line += data[pos];
|
|
Packit |
01d647 |
pos++;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
// skip line ending, if present
|
|
Packit |
01d647 |
if (pos >= size) return pos;
|
|
Packit |
01d647 |
pos++;
|
|
Packit |
01d647 |
if (pos >= size) return pos;
|
|
Packit |
01d647 |
if (data[pos - 1] == '\r' && data[pos] == '\n') pos++;
|
|
Packit |
01d647 |
return pos;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
//! Read the previous line of a buffer, allow for changing line ending style
|
|
Packit |
01d647 |
size_t readPrevLine(std::string& line, const byte* data, size_t startPos, size_t size)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
line.clear();
|
|
Packit |
01d647 |
size_t pos = startPos;
|
|
Packit |
01d647 |
if (pos > size) return pos;
|
|
Packit |
01d647 |
// skip line ending of previous line, if present
|
|
Packit |
01d647 |
if (pos <= 0) return pos;
|
|
Packit |
01d647 |
if (data[pos - 1] == '\r' || data[pos - 1] == '\n') {
|
|
Packit |
01d647 |
pos--;
|
|
Packit |
01d647 |
if (pos <= 0) return pos;
|
|
Packit |
01d647 |
if (data[pos - 1] == '\r' && data[pos] == '\n') {
|
|
Packit |
01d647 |
pos--;
|
|
Packit |
01d647 |
if (pos <= 0) return pos;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
// step through previous line
|
|
Packit |
01d647 |
while (pos >= 1 && data[pos - 1] != '\r' && data[pos - 1] != '\n') {
|
|
Packit |
01d647 |
pos--;
|
|
Packit |
01d647 |
line += data[pos];
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
std::reverse(line.begin(), line.end());
|
|
Packit |
01d647 |
return pos;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
//! Find an XMP block
|
|
Packit |
01d647 |
void findXmp(size_t& xmpPos, size_t& xmpSize, const byte* data, size_t startPos, size_t size, bool write)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
// search for valid XMP header
|
|
Packit |
01d647 |
xmpSize = 0;
|
|
Packit |
01d647 |
for (xmpPos = startPos; xmpPos < size; xmpPos++) {
|
|
Packit |
01d647 |
if (data[xmpPos] != '\x00' && data[xmpPos] != '<') continue;
|
|
Packit |
01d647 |
for (size_t i = 0; i < (sizeof xmpHeaders) / (sizeof *xmpHeaders); i++) {
|
|
Packit |
01d647 |
const std::string &header = xmpHeaders[i];
|
|
Packit |
01d647 |
if (xmpPos + header.size() > size) continue;
|
|
Packit |
01d647 |
if (memcmp(data + xmpPos, header.data(), header.size()) != 0) continue;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "findXmp: Found XMP header at position: " << xmpPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// search for valid XMP trailer
|
|
Packit |
01d647 |
for (size_t trailerPos = xmpPos + header.size(); trailerPos < size; trailerPos++) {
|
|
Packit |
01d647 |
if (data[xmpPos] != '\x00' && data[xmpPos] != '<') continue;
|
|
Packit |
01d647 |
for (size_t j = 0; j < (sizeof xmpTrailers) / (sizeof *xmpTrailers); j++) {
|
|
Packit |
01d647 |
const std::string &trailer = xmpTrailers[j].trailer;
|
|
Packit |
01d647 |
const bool readOnly = xmpTrailers[j].readOnly;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (trailerPos + trailer.size() > size) continue;
|
|
Packit |
01d647 |
if (memcmp(data + trailerPos, trailer.data(), trailer.size()) != 0) continue;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "findXmp: Found XMP trailer at position: " << trailerPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (readOnly) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Unable to handle read-only XMP metadata yet. Please provide your "
|
|
Packit |
01d647 |
"sample EPS file to the Exiv2 project: http://dev.exiv2.org/projects/exiv2\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(write ? kerImageWriteFailed : kerFailedToReadImageData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// search for end of XMP trailer
|
|
Packit |
01d647 |
for (size_t trailerEndPos = trailerPos + trailer.size(); trailerEndPos + xmpTrailerEnd.size() <= size; trailerEndPos++) {
|
|
Packit |
01d647 |
if (memcmp(data + trailerEndPos, xmpTrailerEnd.data(), xmpTrailerEnd.size()) == 0) {
|
|
Packit |
01d647 |
xmpSize = (trailerEndPos + xmpTrailerEnd.size()) - xmpPos;
|
|
Packit |
01d647 |
return;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Found XMP header but incomplete XMP trailer.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(write ? kerImageWriteFailed : kerFailedToReadImageData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Found XMP header but no XMP trailer.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(write ? kerImageWriteFailed : kerFailedToReadImageData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
//! Unified implementation of reading and writing EPS metadata
|
|
Packit |
01d647 |
void readWriteEpsMetadata(BasicIo& io, std::string& xmpPacket, NativePreviewList& nativePreviews, bool write)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
// open input file
|
|
Packit |
01d647 |
if (io.open() != 0) {
|
|
Packit |
01d647 |
throw Error(kerDataSourceOpenFailed, io.path(), strError());
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
IoCloser closer(io);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// read from input file via memory map
|
|
Packit |
01d647 |
const byte *data = io.mmap();
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// default positions and sizes
|
|
Packit |
01d647 |
const size_t size = io.size();
|
|
Packit |
01d647 |
size_t posEps = 0;
|
|
Packit |
01d647 |
size_t posEndEps = size;
|
|
Packit |
01d647 |
uint32_t posWmf = 0;
|
|
Packit |
01d647 |
uint32_t sizeWmf = 0;
|
|
Packit |
01d647 |
uint32_t posTiff = 0;
|
|
Packit |
01d647 |
uint32_t sizeTiff = 0;
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// check for DOS EPS
|
|
Packit |
01d647 |
const bool dosEps = (size >= dosEpsSignature.size() && memcmp(data, dosEpsSignature.data(), dosEpsSignature.size()) == 0);
|
|
Packit |
01d647 |
if (dosEps) {
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Found DOS EPS signature\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (size < 30) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Premature end of file after DOS EPS signature.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(write ? kerImageWriteFailed : kerFailedToReadImageData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
posEps = getULong(data + 4, littleEndian);
|
|
Packit |
01d647 |
posEndEps = getULong(data + 8, littleEndian) + posEps;
|
|
Packit |
01d647 |
posWmf = getULong(data + 12, littleEndian);
|
|
Packit |
01d647 |
sizeWmf = getULong(data + 16, littleEndian);
|
|
Packit |
01d647 |
posTiff = getULong(data + 20, littleEndian);
|
|
Packit |
01d647 |
sizeTiff = getULong(data + 24, littleEndian);
|
|
Packit |
01d647 |
const uint16_t checksum = getUShort(data + 28, littleEndian);
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: EPS section at position " << posEps << ", size " << (posEndEps - posEps) << "\n";
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: WMF section at position " << posWmf << ", size " << sizeWmf << "\n";
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: TIFF section at position " << posTiff << ", size " << sizeTiff << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (checksum != 0xFFFF) {
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: DOS EPS checksum is not FFFF\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (!((posWmf == 0 && sizeWmf == 0) || (posTiff == 0 && sizeTiff == 0))) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "DOS EPS file has both WMF and TIFF section. Only one of those is allowed.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (write) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (sizeWmf == 0 && sizeTiff == 0) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "DOS EPS file has neither WMF nor TIFF section. Exactly one of those is required.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (write) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (posEps < 30 || posEndEps > size) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "DOS EPS file has invalid position (" << posEps << ") or size (" << (posEndEps - posEps) << ") for EPS section.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(write ? kerImageWriteFailed : kerFailedToReadImageData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (sizeWmf != 0 && (posWmf < 30 || posWmf + sizeWmf > size)) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "DOS EPS file has invalid position (" << posWmf << ") or size (" << sizeWmf << ") for WMF section.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (write) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (sizeTiff != 0 && (posTiff < 30 || posTiff + sizeTiff > size)) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "DOS EPS file has invalid position (" << posTiff << ") or size (" << sizeTiff << ") for TIFF section.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (write) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// check first line
|
|
Packit |
01d647 |
std::string firstLine;
|
|
Packit |
01d647 |
const size_t posSecondLine = readLine(firstLine, data, posEps, posEndEps);
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: First line: " << firstLine << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
bool matched = false;
|
|
Packit |
01d647 |
for (size_t i = 0; !matched && i < (sizeof epsFirstLine) / (sizeof *epsFirstLine); i++) {
|
|
Packit |
01d647 |
matched = (firstLine == epsFirstLine[i]);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (!matched) {
|
|
Packit |
01d647 |
throw Error(kerNotAnImage, "EPS");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// determine line ending style of the first line
|
|
Packit |
01d647 |
if (posSecondLine >= posEndEps) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Premature end of file after first line.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(write ? kerImageWriteFailed : kerFailedToReadImageData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
const std::string lineEnding(reinterpret_cast<const char*>(data + posEps + firstLine.size()), posSecondLine - (posEps + firstLine.size()));
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
if (lineEnding == "\n") {
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Line ending style: Unix (LF)\n";
|
|
Packit |
01d647 |
} else if (lineEnding == "\r") {
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Line ending style: Mac (CR)\n";
|
|
Packit |
01d647 |
} else if (lineEnding == "\r\n") {
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Line ending style: DOS (CR LF)\n";
|
|
Packit |
01d647 |
} else {
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Line ending style: (unknown)\n";
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// scan comments
|
|
Packit |
01d647 |
size_t posLanguageLevel = posEndEps;
|
|
Packit |
01d647 |
size_t posContainsXmp = posEndEps;
|
|
Packit |
01d647 |
size_t posPages = posEndEps;
|
|
Packit |
01d647 |
size_t posExiv2Version = posEndEps;
|
|
Packit |
01d647 |
size_t posExiv2Website = posEndEps;
|
|
Packit |
01d647 |
size_t posEndComments = posEndEps;
|
|
Packit |
01d647 |
size_t posAi7Thumbnail = posEndEps;
|
|
Packit |
01d647 |
size_t posAi7ThumbnailEndData = posEndEps;
|
|
Packit |
01d647 |
size_t posBeginPhotoshop = posEndEps;
|
|
Packit |
01d647 |
size_t posEndPhotoshop = posEndEps;
|
|
Packit |
01d647 |
size_t posPage = posEndEps;
|
|
Packit |
01d647 |
size_t posBeginPageSetup = posEndEps;
|
|
Packit |
01d647 |
size_t posEndPageSetup = posEndEps;
|
|
Packit |
01d647 |
size_t posPageTrailer = posEndEps;
|
|
Packit |
01d647 |
size_t posEof = posEndEps;
|
|
Packit |
01d647 |
std::vector<std::pair<size_t, size_t> > removableEmbeddings;
|
|
Packit |
01d647 |
unsigned int depth = 0;
|
|
Packit |
01d647 |
const unsigned int maxDepth = UINT_MAX;
|
|
Packit |
01d647 |
bool illustrator8 = false;
|
|
Packit |
01d647 |
bool corelDraw = false;
|
|
Packit |
01d647 |
bool implicitPage = false;
|
|
Packit |
01d647 |
bool implicitPageSetup = false;
|
|
Packit |
01d647 |
bool implicitPageTrailer = false;
|
|
Packit |
01d647 |
bool inDefaultsPreviewPrologSetup = false;
|
|
Packit |
01d647 |
bool inRemovableEmbedding = false;
|
|
Packit |
01d647 |
std::string removableEmbeddingEndLine;
|
|
Packit |
01d647 |
unsigned int removableEmbeddingsWithUnmarkedTrailer = 0;
|
|
Packit |
01d647 |
for (size_t pos = posEps; pos < posEof;) {
|
|
Packit |
01d647 |
const size_t startPos = pos;
|
|
Packit |
01d647 |
std::string line;
|
|
Packit |
01d647 |
pos = readLine(line, data, startPos, posEndEps);
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
bool significantLine = true;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
// nested documents
|
|
Packit |
01d647 |
if (posPage == posEndEps && (startsWith(line, "%%IncludeDocument:") || startsWith(line, "%%BeginDocument:"))) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Nested document at invalid position: " << startPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(write ? kerImageWriteFailed : kerFailedToReadImageData);
|
|
Packit |
01d647 |
} else if (startsWith(line, "%%BeginDocument:")) {
|
|
Packit |
01d647 |
if (depth == maxDepth) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Document too deeply nested at position: " << startPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(write ? kerImageWriteFailed : kerFailedToReadImageData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
depth++;
|
|
Packit |
01d647 |
} else if (startsWith(line, "%%EndDocument")) {
|
|
Packit |
01d647 |
if (depth == 0) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Unmatched EndDocument at position: " << startPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(write ? kerImageWriteFailed : kerFailedToReadImageData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
depth--;
|
|
Packit |
01d647 |
} else {
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
significantLine = false;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
if (significantLine) {
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Found significant line \"" << line << "\" at position: " << startPos << "\n";
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
significantLine = true;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (depth != 0) continue;
|
|
Packit |
01d647 |
// explicit "Begin" comments
|
|
Packit |
01d647 |
if (startsWith(line, "%%BeginPreview:")) {
|
|
Packit |
01d647 |
inDefaultsPreviewPrologSetup = true;
|
|
Packit |
01d647 |
} else if (line == "%%BeginDefaults") {
|
|
Packit |
01d647 |
inDefaultsPreviewPrologSetup = true;
|
|
Packit |
01d647 |
} else if (line == "%%BeginProlog") {
|
|
Packit |
01d647 |
inDefaultsPreviewPrologSetup = true;
|
|
Packit |
01d647 |
} else if (line == "%%BeginSetup") {
|
|
Packit |
01d647 |
inDefaultsPreviewPrologSetup = true;
|
|
Packit |
01d647 |
} else if (posPage == posEndEps && startsWith(line, "%%Page:")) {
|
|
Packit |
01d647 |
posPage = startPos;
|
|
Packit |
01d647 |
} else if (posPage != posEndEps && startsWith(line, "%%Page:")) {
|
|
Packit |
01d647 |
if (implicitPage) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Page at position " << startPos << " conflicts with implicit page at position: " << posPage << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(write ? kerImageWriteFailed : kerFailedToReadImageData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Unable to handle multiple PostScript pages. Found second page at position: " << startPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(write ? kerImageWriteFailed : kerFailedToReadImageData);
|
|
Packit |
01d647 |
} else if (line == "%%BeginPageSetup") {
|
|
Packit |
01d647 |
posBeginPageSetup = startPos;
|
|
Packit |
01d647 |
} else if (!inRemovableEmbedding && line == "%Exiv2BeginXMP: Before %%EndPageSetup") {
|
|
Packit |
01d647 |
inRemovableEmbedding = true;
|
|
Packit |
01d647 |
removableEmbeddings.push_back(std::make_pair(startPos, startPos));
|
|
Packit |
01d647 |
removableEmbeddingEndLine = "%Exiv2EndXMP";
|
|
Packit |
01d647 |
} else if (!inRemovableEmbedding && line == "%Exiv2BeginXMP: After %%PageTrailer") {
|
|
Packit |
01d647 |
inRemovableEmbedding = true;
|
|
Packit |
01d647 |
removableEmbeddings.push_back(std::make_pair(startPos, startPos));
|
|
Packit |
01d647 |
removableEmbeddingEndLine = "%Exiv2EndXMP";
|
|
Packit |
01d647 |
} else if (!inRemovableEmbedding && line == "%ADOBeginClientInjection: PageSetup End \"AI11EPS\"") {
|
|
Packit |
01d647 |
inRemovableEmbedding = true;
|
|
Packit |
01d647 |
removableEmbeddings.push_back(std::make_pair(startPos, startPos));
|
|
Packit |
01d647 |
removableEmbeddingEndLine = "%ADOEndClientInjection: PageSetup End \"AI11EPS\"";
|
|
Packit |
01d647 |
} else if (!inRemovableEmbedding && line == "%ADOBeginClientInjection: PageTrailer Start \"AI11EPS\"") {
|
|
Packit |
01d647 |
inRemovableEmbedding = true;
|
|
Packit |
01d647 |
removableEmbeddings.push_back(std::make_pair(startPos, startPos));
|
|
Packit |
01d647 |
removableEmbeddingEndLine = "%ADOEndClientInjection: PageTrailer Start \"AI11EPS\"";
|
|
Packit |
01d647 |
} else if (!inRemovableEmbedding && line == "%begin_xml_code") {
|
|
Packit |
01d647 |
inRemovableEmbedding = true;
|
|
Packit |
01d647 |
removableEmbeddings.push_back(std::make_pair(startPos, startPos));
|
|
Packit |
01d647 |
removableEmbeddingEndLine = "%end_xml_code";
|
|
Packit |
01d647 |
removableEmbeddingsWithUnmarkedTrailer++;
|
|
Packit |
01d647 |
} else {
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
significantLine = false;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
if (significantLine) {
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Found significant line \"" << line << "\" at position: " << startPos << "\n";
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
significantLine = true;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
// implicit comments
|
|
Packit |
01d647 |
if (line == "%%EOF" || line == "%begin_xml_code" || !(line.size() >= 2 && line[0] == '%' && '\x21' <= line[1] && line[1] <= '\x7e')) {
|
|
Packit |
01d647 |
if (posEndComments == posEndEps) {
|
|
Packit |
01d647 |
posEndComments = startPos;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Found implicit EndComments at position: " << startPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (posPage == posEndEps && posEndComments != posEndEps && !inDefaultsPreviewPrologSetup && !inRemovableEmbedding && !onlyWhitespaces(line)) {
|
|
Packit |
01d647 |
posPage = startPos;
|
|
Packit |
01d647 |
implicitPage = true;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Found implicit Page at position: " << startPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (posBeginPageSetup == posEndEps && (implicitPage || (posPage != posEndEps && !inRemovableEmbedding && line.size() >= 1 && line[0] != '%'))) {
|
|
Packit |
01d647 |
posBeginPageSetup = startPos;
|
|
Packit |
01d647 |
implicitPageSetup = true;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Found implicit BeginPageSetup at position: " << startPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (posEndPageSetup == posEndEps && implicitPageSetup && !inRemovableEmbedding && line.size() >= 1 && line[0] != '%') {
|
|
Packit |
01d647 |
posEndPageSetup = startPos;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Found implicit EndPageSetup at position: " << startPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (line.size() >= 1 && line[0] != '%') continue; // performance optimization
|
|
Packit |
01d647 |
if (line == "%%EOF" || line == "%%Trailer" || line == "%%PageTrailer") {
|
|
Packit |
01d647 |
if (posBeginPageSetup == posEndEps) {
|
|
Packit |
01d647 |
posBeginPageSetup = startPos;
|
|
Packit |
01d647 |
implicitPageSetup = true;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Found implicit BeginPageSetup at position: " << startPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (posEndPageSetup == posEndEps) {
|
|
Packit |
01d647 |
posEndPageSetup = startPos;
|
|
Packit |
01d647 |
implicitPageSetup = true;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Found implicit EndPageSetup at position: " << startPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (line == "%%EOF" || line == "%%Trailer") {
|
|
Packit |
01d647 |
if (posPageTrailer == posEndEps) {
|
|
Packit |
01d647 |
posPageTrailer = startPos;
|
|
Packit |
01d647 |
implicitPageTrailer = true;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Found implicit PageTrailer at position: " << startPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
// remaining explicit comments
|
|
Packit |
01d647 |
if (posEndComments == posEndEps && posLanguageLevel == posEndEps && startsWith(line, "%%LanguageLevel:")) {
|
|
Packit |
01d647 |
posLanguageLevel = startPos;
|
|
Packit |
01d647 |
} else if (posEndComments == posEndEps && posContainsXmp == posEndEps && startsWith(line, "%ADO_ContainsXMP:")) {
|
|
Packit |
01d647 |
posContainsXmp = startPos;
|
|
Packit |
01d647 |
} else if (posEndComments == posEndEps && posPages == posEndEps && startsWith(line, "%%Pages:")) {
|
|
Packit |
01d647 |
posPages = startPos;
|
|
Packit |
01d647 |
} else if (posEndComments == posEndEps && posExiv2Version == posEndEps && startsWith(line, "%Exiv2Version:")) {
|
|
Packit |
01d647 |
posExiv2Version = startPos;
|
|
Packit |
01d647 |
} else if (posEndComments == posEndEps && posExiv2Website == posEndEps && startsWith(line, "%Exiv2Website:")) {
|
|
Packit |
01d647 |
posExiv2Website = startPos;
|
|
Packit |
01d647 |
} else if (posEndComments == posEndEps && startsWith(line, "%%Creator: Adobe Illustrator") && firstLine == "%!PS-Adobe-3.0 EPSF-3.0") {
|
|
Packit |
01d647 |
illustrator8 = true;
|
|
Packit |
01d647 |
} else if (posEndComments == posEndEps && startsWith(line, "%AI7_Thumbnail:")) {
|
|
Packit |
01d647 |
posAi7Thumbnail = startPos;
|
|
Packit |
01d647 |
} else if (posEndComments == posEndEps && posAi7Thumbnail != posEndEps && posAi7ThumbnailEndData == posEndEps && line == "%%EndData") {
|
|
Packit |
01d647 |
posAi7ThumbnailEndData = startPos;
|
|
Packit |
01d647 |
} else if (posEndComments == posEndEps && line == "%%EndComments") {
|
|
Packit |
01d647 |
posEndComments = startPos;
|
|
Packit |
01d647 |
} else if (inDefaultsPreviewPrologSetup && startsWith(line, "%%BeginResource: procset wCorel")) {
|
|
Packit |
01d647 |
corelDraw = true;
|
|
Packit |
01d647 |
} else if (line == "%%EndPreview") {
|
|
Packit |
01d647 |
inDefaultsPreviewPrologSetup = false;
|
|
Packit |
01d647 |
} else if (line == "%%EndDefaults") {
|
|
Packit |
01d647 |
inDefaultsPreviewPrologSetup = false;
|
|
Packit |
01d647 |
} else if (line == "%%EndProlog") {
|
|
Packit |
01d647 |
inDefaultsPreviewPrologSetup = false;
|
|
Packit |
01d647 |
} else if (line == "%%EndSetup") {
|
|
Packit |
01d647 |
inDefaultsPreviewPrologSetup = false;
|
|
Packit |
01d647 |
} else if (posEndPageSetup == posEndEps && line == "%%EndPageSetup") {
|
|
Packit |
01d647 |
posEndPageSetup = startPos;
|
|
Packit |
01d647 |
} else if (posPageTrailer == posEndEps && line == "%%PageTrailer") {
|
|
Packit |
01d647 |
posPageTrailer = startPos;
|
|
Packit |
01d647 |
} else if (posBeginPhotoshop == posEndEps && startsWith(line, "%BeginPhotoshop:")) {
|
|
Packit |
01d647 |
posBeginPhotoshop = pos;
|
|
Packit |
01d647 |
} else if (posBeginPhotoshop != posEndEps && posEndPhotoshop == posEndEps && line == "%EndPhotoshop") {
|
|
Packit |
01d647 |
posEndPhotoshop = startPos;
|
|
Packit |
01d647 |
} else if (inRemovableEmbedding && line == removableEmbeddingEndLine) {
|
|
Packit |
01d647 |
inRemovableEmbedding = false;
|
|
Packit |
01d647 |
removableEmbeddings.back().second = pos;
|
|
Packit |
01d647 |
} else if (line == "%%EOF") {
|
|
Packit |
01d647 |
posEof = startPos;
|
|
Packit |
01d647 |
} else {
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
significantLine = false;
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
if (significantLine) {
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Found significant line \"" << line << "\" at position: " << startPos << "\n";
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// check for unfinished nested documents
|
|
Packit |
01d647 |
if (depth != 0) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Unmatched BeginDocument (" << depth << "x)\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(write ? kerImageWriteFailed : kerFailedToReadImageData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// look for the unmarked trailers of some removable XMP embeddings
|
|
Packit |
01d647 |
size_t posXmpTrailerEnd = posEof;
|
|
Packit |
01d647 |
for (size_t i = 0; i < removableEmbeddingsWithUnmarkedTrailer; i++) {
|
|
Packit |
01d647 |
std::string line1;
|
|
Packit |
01d647 |
const size_t posLine1 = readPrevLine(line1, data, posXmpTrailerEnd, posEndEps);
|
|
Packit |
01d647 |
std::string line2;
|
|
Packit |
01d647 |
const size_t posLine2 = readPrevLine(line2, data, posLine1, posEndEps);
|
|
Packit |
01d647 |
size_t posXmpTrailer;
|
|
Packit |
01d647 |
if (line1 == "[/EMC pdfmark") { // Exiftool style
|
|
Packit |
01d647 |
posXmpTrailer = posLine1;
|
|
Packit |
01d647 |
} else if (line1 == "[/NamespacePop pdfmark" &&
|
|
Packit |
01d647 |
line2 == "[{nextImage} 1 dict begin /Metadata {photoshop_metadata_stream} def currentdict end /PUT pdfmark") { // Photoshop style
|
|
Packit |
01d647 |
posXmpTrailer = posLine2;
|
|
Packit |
01d647 |
} else {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Unable to find XMP embedding trailer ending at position: " << posXmpTrailerEnd << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (write) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
removableEmbeddings.push_back(std::make_pair(posXmpTrailer, posXmpTrailerEnd));
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Recognized unmarked trailer of removable XMP embedding at "
|
|
Packit |
01d647 |
"[" << removableEmbeddings.back().first << "," << removableEmbeddings.back().second << ")"
|
|
Packit |
01d647 |
"\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
posXmpTrailerEnd = posXmpTrailer;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// interpret comment "%ADO_ContainsXMP:"
|
|
Packit |
01d647 |
std::string line;
|
|
Packit |
01d647 |
readLine(line, data, posContainsXmp, posEndEps);
|
|
Packit |
01d647 |
bool containsXmp;
|
|
Packit |
01d647 |
if (line == "%ADO_ContainsXMP: MainFirst" || line == "%ADO_ContainsXMP:MainFirst") {
|
|
Packit |
01d647 |
containsXmp = true;
|
|
Packit |
01d647 |
} else if (line == "" || line == "%ADO_ContainsXMP: NoMain" || line == "%ADO_ContainsXMP:NoMain") {
|
|
Packit |
01d647 |
containsXmp = false;
|
|
Packit |
01d647 |
} else {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Invalid line \"" << line << "\" at position: " << posContainsXmp << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(write ? kerImageWriteFailed : kerFailedToReadImageData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
const bool deleteXmp = (write && xmpPacket.size() == 0);
|
|
Packit |
01d647 |
bool fixBeginXmlPacket = false;
|
|
Packit |
01d647 |
bool useFlexibleEmbedding = false;
|
|
Packit |
01d647 |
size_t xmpPos = posEndEps;
|
|
Packit |
01d647 |
size_t xmpSize = 0;
|
|
Packit |
01d647 |
if (containsXmp) {
|
|
Packit |
01d647 |
// search for XMP metadata
|
|
Packit |
01d647 |
findXmp(xmpPos, xmpSize, data, posEps, posEndEps, write);
|
|
Packit |
01d647 |
if (xmpPos == posEndEps) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Unable to find XMP metadata as announced at position: " << posContainsXmp << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
// check embedding of XMP metadata
|
|
Packit |
01d647 |
const size_t posLineAfterXmp = readLine(line, data, xmpPos + xmpSize, posEndEps);
|
|
Packit |
01d647 |
if (line != "") {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Unexpected " << line.size() << " bytes of data after XMP at position: " << (xmpPos + xmpSize) << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
} else if (!deleteXmp) {
|
|
Packit |
01d647 |
readLine(line, data, posLineAfterXmp, posEndEps);
|
|
Packit |
01d647 |
if (line == "% &&end XMP packet marker&&" || line == "% &&end XMP packet marker&&") {
|
|
Packit |
01d647 |
useFlexibleEmbedding = true;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (useFlexibleEmbedding) {
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Using flexible XMP embedding\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
const size_t posBeginXmlPacket = readPrevLine(line, data, xmpPos, posEndEps);
|
|
Packit |
01d647 |
if (startsWith(line, "%begin_xml_packet:")) {
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: XMP embedding contains %begin_xml_packet\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (write) {
|
|
Packit |
01d647 |
fixBeginXmlPacket = true;
|
|
Packit |
01d647 |
xmpSize += (xmpPos - posBeginXmlPacket);
|
|
Packit |
01d647 |
xmpPos = posBeginXmlPacket;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
} else if (posBeginPhotoshop != posEndEps) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Missing %begin_xml_packet in Photoshop EPS at position: " << xmpPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (write) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (!useFlexibleEmbedding) {
|
|
Packit |
01d647 |
// check if there are irremovable XMP metadata blocks before EndPageSetup
|
|
Packit |
01d647 |
size_t posOtherXmp = containsXmp ? xmpPos : posEps;
|
|
Packit |
01d647 |
size_t sizeOtherXmp = 0;
|
|
Packit |
01d647 |
for (;;) {
|
|
Packit |
01d647 |
findXmp(posOtherXmp, sizeOtherXmp, data, posOtherXmp + sizeOtherXmp, posEndPageSetup, write);
|
|
Packit |
01d647 |
if (posOtherXmp >= posEndPageSetup) break;
|
|
Packit |
01d647 |
bool isRemovableEmbedding = false;
|
|
Packit |
01d647 |
for (std::vector<std::pair<size_t, size_t> >::const_iterator e = removableEmbeddings.begin(); e != removableEmbeddings.end(); ++e) {
|
|
Packit |
01d647 |
if (e->first <= posOtherXmp && posOtherXmp < e->second) {
|
|
Packit |
01d647 |
isRemovableEmbedding = true;
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (!isRemovableEmbedding) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "XMP metadata block is not removable at position: " << posOtherXmp << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (write) throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
if (!write) {
|
|
Packit |
01d647 |
// copy XMP metadata
|
|
Packit |
01d647 |
xmpPacket.assign(reinterpret_cast<const char*>(data + xmpPos), xmpSize);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// native previews
|
|
Packit |
01d647 |
nativePreviews.clear();
|
|
Packit |
01d647 |
if (posAi7ThumbnailEndData != posEndEps) {
|
|
Packit |
01d647 |
NativePreview nativePreview;
|
|
Packit |
01d647 |
std::string dummy;
|
|
Packit |
01d647 |
std::string lineAi7Thumbnail;
|
|
Packit |
01d647 |
const size_t posBeginData = readLine(lineAi7Thumbnail, data, posAi7Thumbnail, posEndEps);
|
|
Packit |
01d647 |
std::istringstream lineStreamAi7Thumbnail(lineAi7Thumbnail);
|
|
Packit |
01d647 |
lineStreamAi7Thumbnail >> dummy;
|
|
Packit |
01d647 |
lineStreamAi7Thumbnail >> nativePreview.width_;
|
|
Packit |
01d647 |
lineStreamAi7Thumbnail >> nativePreview.height_;
|
|
Packit |
01d647 |
std::string depth;
|
|
Packit |
01d647 |
lineStreamAi7Thumbnail >> depth;
|
|
Packit |
01d647 |
std::string lineBeginData;
|
|
Packit |
01d647 |
const size_t posAfterBeginData = readLine(lineBeginData, data, posBeginData, posEndEps);
|
|
Packit |
01d647 |
std::istringstream lineStreamBeginData(lineBeginData);
|
|
Packit |
01d647 |
std::string beginData;
|
|
Packit |
01d647 |
lineStreamBeginData >> beginData;
|
|
Packit |
01d647 |
lineStreamBeginData >> dummy;
|
|
Packit |
01d647 |
std::string type;
|
|
Packit |
01d647 |
lineStreamBeginData >> type;
|
|
Packit |
01d647 |
nativePreview.position_ = static_cast<long>(posAfterBeginData);
|
|
Packit |
01d647 |
nativePreview.size_ = static_cast<uint32_t>(posAi7ThumbnailEndData - posAfterBeginData);
|
|
Packit |
01d647 |
nativePreview.filter_ = "hex-ai7thumbnail-pnm";
|
|
Packit |
01d647 |
nativePreview.mimeType_ = "image/x-portable-anymap";
|
|
Packit |
01d647 |
if (depth != "8") {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Unable to handle Illustrator thumbnail depth: " << depth << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
} else if (beginData != "%%BeginData:") {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Unable to handle Illustrator thumbnail data section: " << lineBeginData << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
} else if (type != "Hex") {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Unable to handle Illustrator thumbnail data type: " << type << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
} else {
|
|
Packit |
01d647 |
nativePreviews.push_back(nativePreview);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (posEndPhotoshop != posEndEps) {
|
|
Packit |
01d647 |
NativePreview nativePreview;
|
|
Packit |
01d647 |
nativePreview.position_ = static_cast<long>(posBeginPhotoshop);
|
|
Packit |
01d647 |
nativePreview.size_ = static_cast<uint32_t>(posEndPhotoshop - posBeginPhotoshop);
|
|
Packit |
01d647 |
nativePreview.width_ = 0;
|
|
Packit |
01d647 |
nativePreview.height_ = 0;
|
|
Packit |
01d647 |
nativePreview.filter_ = "hex-irb";
|
|
Packit |
01d647 |
nativePreview.mimeType_ = "image/jpeg";
|
|
Packit |
01d647 |
nativePreviews.push_back(nativePreview);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (sizeWmf != 0) {
|
|
Packit |
01d647 |
NativePreview nativePreview;
|
|
Packit |
01d647 |
nativePreview.position_ = static_cast<long>(posWmf);
|
|
Packit |
01d647 |
nativePreview.size_ = sizeWmf;
|
|
Packit |
01d647 |
nativePreview.width_ = 0;
|
|
Packit |
01d647 |
nativePreview.height_ = 0;
|
|
Packit |
01d647 |
nativePreview.filter_ = "";
|
|
Packit |
01d647 |
nativePreview.mimeType_ = "image/x-wmf";
|
|
Packit |
01d647 |
nativePreviews.push_back(nativePreview);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (sizeTiff != 0) {
|
|
Packit |
01d647 |
NativePreview nativePreview;
|
|
Packit |
01d647 |
nativePreview.position_ = static_cast<long>(posTiff);
|
|
Packit |
01d647 |
nativePreview.size_ = sizeTiff;
|
|
Packit |
01d647 |
nativePreview.width_ = 0;
|
|
Packit |
01d647 |
nativePreview.height_ = 0;
|
|
Packit |
01d647 |
nativePreview.filter_ = "";
|
|
Packit |
01d647 |
nativePreview.mimeType_ = "image/tiff";
|
|
Packit |
01d647 |
nativePreviews.push_back(nativePreview);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
} else {
|
|
Packit |
01d647 |
// check for Adobe Illustrator 8.0 or older
|
|
Packit |
01d647 |
if (illustrator8) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Unable to write to EPS files created by Adobe Illustrator 8.0 or older.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// create temporary output file
|
|
Packit |
01d647 |
BasicIo::AutoPtr tempIo(new MemIo);
|
|
Packit |
01d647 |
assert (tempIo.get() != 0);
|
|
Packit |
01d647 |
if (!tempIo->isopen()) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Unable to create temporary file for writing.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Created temporary file " << tempIo->path() << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// sort all positions
|
|
Packit |
01d647 |
std::vector<size_t> positions;
|
|
Packit |
01d647 |
positions.push_back(posLanguageLevel);
|
|
Packit |
01d647 |
positions.push_back(posContainsXmp);
|
|
Packit |
01d647 |
positions.push_back(posPages);
|
|
Packit |
01d647 |
positions.push_back(posExiv2Version);
|
|
Packit |
01d647 |
positions.push_back(posExiv2Website);
|
|
Packit |
01d647 |
positions.push_back(posEndComments);
|
|
Packit |
01d647 |
positions.push_back(posPage);
|
|
Packit |
01d647 |
positions.push_back(posBeginPageSetup);
|
|
Packit |
01d647 |
positions.push_back(posEndPageSetup);
|
|
Packit |
01d647 |
positions.push_back(posPageTrailer);
|
|
Packit |
01d647 |
positions.push_back(posEof);
|
|
Packit |
01d647 |
positions.push_back(posEndEps);
|
|
Packit |
01d647 |
if (useFlexibleEmbedding) {
|
|
Packit |
01d647 |
positions.push_back(xmpPos);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
for (std::vector<std::pair<size_t, size_t> >::const_iterator e = removableEmbeddings.begin(); e != removableEmbeddings.end(); ++e) {
|
|
Packit |
01d647 |
positions.push_back(e->first);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
std::sort(positions.begin(), positions.end());
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// assemble result EPS document
|
|
Packit |
01d647 |
if (dosEps) {
|
|
Packit |
01d647 |
// DOS EPS header will be written afterwards
|
|
Packit |
01d647 |
writeTemp(*tempIo, std::string(30, '\x00'));
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
const std::string containsXmpLine = deleteXmp ? "%ADO_ContainsXMP: NoMain" : "%ADO_ContainsXMP: MainFirst";
|
|
Packit |
01d647 |
const uint32_t posEpsNew = posTemp(*tempIo);
|
|
Packit |
01d647 |
size_t prevPos = posEps;
|
|
Packit |
01d647 |
size_t prevSkipPos = prevPos;
|
|
Packit |
01d647 |
for (std::vector<size_t>::const_iterator i = positions.begin(); i != positions.end(); ++i) {
|
|
Packit |
01d647 |
const size_t pos = *i;
|
|
Packit |
01d647 |
if (pos == prevPos) continue;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Writing at " << pos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (pos < prevSkipPos) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Internal error while assembling the result EPS document: "
|
|
Packit |
01d647 |
"Unable to continue at position " << pos << " after skipping to position " << prevSkipPos << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
writeTemp(*tempIo, data + prevSkipPos, pos - prevSkipPos);
|
|
Packit |
01d647 |
const size_t posLineEnd = readLine(line, data, pos, posEndEps);
|
|
Packit |
01d647 |
size_t skipPos = pos;
|
|
Packit |
01d647 |
// add last line ending if necessary
|
|
Packit |
01d647 |
if (pos == posEndEps && pos >= 1 && data[pos - 1] != '\r' && data[pos - 1] != '\n') {
|
|
Packit |
01d647 |
writeTemp(*tempIo, lineEnding);
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Added missing line ending of last line\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
// update and complement DSC comments
|
|
Packit |
01d647 |
if (pos == posLanguageLevel && posLanguageLevel != posEndEps && !deleteXmp && !useFlexibleEmbedding) {
|
|
Packit |
01d647 |
if (line == "%%LanguageLevel:1" || line == "%%LanguageLevel: 1") {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%%LanguageLevel: 2" + lineEnding);
|
|
Packit |
01d647 |
skipPos = posLineEnd;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Skipping to " << skipPos << " at " << __FILE__ << ":" << __LINE__ << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (pos == posContainsXmp && posContainsXmp != posEndEps) {
|
|
Packit |
01d647 |
if (line != containsXmpLine) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, containsXmpLine + lineEnding);
|
|
Packit |
01d647 |
skipPos = posLineEnd;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Skipping to " << skipPos << " at " << __FILE__ << ":" << __LINE__ << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (pos == posExiv2Version && posExiv2Version != posEndEps) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%Exiv2Version: " + versionNumberHexString() + lineEnding);
|
|
Packit |
01d647 |
skipPos = posLineEnd;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Skipping to " << skipPos << " at " << __FILE__ << ":" << __LINE__ << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (pos == posExiv2Website && posExiv2Website != posEndEps) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%Exiv2Website: http://www.exiv2.org/" + lineEnding);
|
|
Packit |
01d647 |
skipPos = posLineEnd;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Skipping to " << skipPos << " at " << __FILE__ << ":" << __LINE__ << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (pos == posEndComments) {
|
|
Packit |
01d647 |
if (posLanguageLevel == posEndEps && !deleteXmp && !useFlexibleEmbedding) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%%LanguageLevel: 2" + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (posContainsXmp == posEndEps) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, containsXmpLine + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (posPages == posEndEps) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%%Pages: 1" + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (posExiv2Version == posEndEps) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%Exiv2Version: " + versionNumberHexString() + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (posExiv2Website == posEndEps) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%Exiv2Website: http://www.exiv2.org/" + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
readLine(line, data, posEndComments, posEndEps);
|
|
Packit |
01d647 |
if (line != "%%EndComments") {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%%EndComments" + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (pos == posPage) {
|
|
Packit |
01d647 |
if (!startsWith(line, "%%Page:")) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%%Page: 1 1" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%%EndPageComments" + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (pos == posBeginPageSetup) {
|
|
Packit |
01d647 |
if (line != "%%BeginPageSetup") {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%%BeginPageSetup" + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (useFlexibleEmbedding) {
|
|
Packit |
01d647 |
// insert XMP metadata into existing flexible embedding
|
|
Packit |
01d647 |
if (pos == xmpPos) {
|
|
Packit |
01d647 |
if (fixBeginXmlPacket) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%begin_xml_packet: " + toString(xmpPacket.size()) + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
writeTemp(*tempIo, xmpPacket);
|
|
Packit |
01d647 |
skipPos += xmpSize;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Skipping to " << skipPos << " at " << __FILE__ << ":" << __LINE__ << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (!useFlexibleEmbedding) {
|
|
Packit |
01d647 |
// remove preceding embedding(s)
|
|
Packit |
01d647 |
for (std::vector<std::pair<size_t, size_t> >::const_iterator e = removableEmbeddings.begin(); e != removableEmbeddings.end(); ++e) {
|
|
Packit |
01d647 |
if (pos == e->first) {
|
|
Packit |
01d647 |
skipPos = e->second;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Skipping to " << skipPos << " at " << __FILE__ << ":" << __LINE__ << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
break;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
// insert XMP metadata with new flexible embedding, if necessary
|
|
Packit |
01d647 |
if (pos == posEndPageSetup && !deleteXmp) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%Exiv2BeginXMP: Before %%EndPageSetup" + lineEnding);
|
|
Packit |
01d647 |
if (corelDraw) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%Exiv2Notice: The following line is needed by CorelDRAW." + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "@rs" + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (posBeginPhotoshop != posEndEps) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%Exiv2Notice: The following line is needed by Photoshop." + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%begin_xml_code" + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
writeTemp(*tempIo, "/currentdistillerparams where" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "{pop currentdistillerparams /CoreDistVersion get 5000 lt} {true} ifelse" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "{userdict /Exiv2_pdfmark /cleartomark load put" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, " userdict /Exiv2_metafile_pdfmark {flushfile cleartomark} bind put}" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "{userdict /Exiv2_pdfmark /pdfmark load put" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, " userdict /Exiv2_metafile_pdfmark {/PUT pdfmark} bind put} ifelse" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "[/NamespacePush Exiv2_pdfmark" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "[/_objdef {Exiv2_metadata_stream} /type /stream /OBJ Exiv2_pdfmark" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "[{Exiv2_metadata_stream} 2 dict begin" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, " /Type /Metadata def /Subtype /XML def currentdict end /PUT Exiv2_pdfmark" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "[{Exiv2_metadata_stream}" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, " currentfile 0 (% &&end XMP packet marker&&)" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, " /SubFileDecode filter Exiv2_metafile_pdfmark" + lineEnding);
|
|
Packit |
01d647 |
if (posBeginPhotoshop != posEndEps) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%Exiv2Notice: The following line is needed by Photoshop. "
|
|
Packit |
01d647 |
"Parameter must be exact size of XMP metadata." + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%begin_xml_packet: " + toString(xmpPacket.size()) + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
writeTemp(*tempIo, xmpPacket);
|
|
Packit |
01d647 |
writeTemp(*tempIo, lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "% &&end XMP packet marker&&" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "[/Document 1 dict begin" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, " /Metadata {Exiv2_metadata_stream} def currentdict end /BDC Exiv2_pdfmark" + lineEnding);
|
|
Packit |
01d647 |
if (posBeginPhotoshop != posEndEps) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%Exiv2Notice: The following line is needed by Photoshop." + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%end_xml_code" + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (corelDraw) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%Exiv2Notice: The following line is needed by CorelDRAW." + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "@sv" + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%Exiv2EndXMP" + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (pos == posEndPageSetup) {
|
|
Packit |
01d647 |
if (line != "%%EndPageSetup") {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%%EndPageSetup" + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
if (!useFlexibleEmbedding) {
|
|
Packit |
01d647 |
if (pos == posPageTrailer && !deleteXmp) {
|
|
Packit |
01d647 |
if (!implicitPageTrailer) {
|
|
Packit |
01d647 |
skipPos = posLineEnd;
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: Skipping to " << skipPos << " at " << __FILE__ << ":" << __LINE__ << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%%PageTrailer" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%Exiv2BeginXMP: After %%PageTrailer" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "[/EMC Exiv2_pdfmark" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "[/NamespacePop Exiv2_pdfmark" + lineEnding);
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%Exiv2EndXMP" + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
// add EOF comment if necessary
|
|
Packit |
01d647 |
if (pos == posEndEps && posEof == posEndEps) {
|
|
Packit |
01d647 |
writeTemp(*tempIo, "%%EOF" + lineEnding);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
prevPos = pos;
|
|
Packit |
01d647 |
prevSkipPos = skipPos;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
const uint32_t posEndEpsNew = posTemp(*tempIo);
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: New EPS size: " << (posEndEpsNew - posEpsNew) << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
if (dosEps) {
|
|
Packit |
01d647 |
// write WMF and/or TIFF section if present
|
|
Packit |
01d647 |
writeTemp(*tempIo, data + posWmf, sizeWmf);
|
|
Packit |
01d647 |
writeTemp(*tempIo, data + posTiff, sizeTiff);
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "readWriteEpsMetadata: New DOS EPS total size: " << posTemp(*tempIo) << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
// write DOS EPS header
|
|
Packit |
01d647 |
if (tempIo->seek(0, BasicIo::beg) != 0) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Internal error while seeking in temporary file.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
byte dosEpsHeader[30];
|
|
Packit |
01d647 |
dosEpsSignature.copy(reinterpret_cast<char*>(dosEpsHeader), dosEpsSignature.size());
|
|
Packit |
01d647 |
ul2Data(dosEpsHeader + 4, posEpsNew, littleEndian);
|
|
Packit |
01d647 |
ul2Data(dosEpsHeader + 8, posEndEpsNew - posEpsNew, littleEndian);
|
|
Packit |
01d647 |
ul2Data(dosEpsHeader + 12, sizeWmf == 0 ? 0 : posEndEpsNew, littleEndian);
|
|
Packit |
01d647 |
ul2Data(dosEpsHeader + 16, sizeWmf, littleEndian);
|
|
Packit |
01d647 |
ul2Data(dosEpsHeader + 20, sizeTiff == 0 ? 0 : posEndEpsNew + sizeWmf, littleEndian);
|
|
Packit |
01d647 |
ul2Data(dosEpsHeader + 24, sizeTiff, littleEndian);
|
|
Packit |
01d647 |
us2Data(dosEpsHeader + 28, 0xFFFF, littleEndian);
|
|
Packit |
01d647 |
writeTemp(*tempIo, dosEpsHeader, sizeof(dosEpsHeader));
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// copy temporary file to real output file
|
|
Packit |
01d647 |
io.close();
|
|
Packit |
01d647 |
io.transfer(*tempIo);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
} // namespace
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// *****************************************************************************
|
|
Packit |
01d647 |
// class member definitions
|
|
Packit |
01d647 |
namespace Exiv2
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
EpsImage::EpsImage(BasicIo::AutoPtr io, bool create)
|
|
Packit |
01d647 |
: Image(ImageType::eps, mdXmp, io)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
//LogMsg::setLevel(LogMsg::debug);
|
|
Packit |
01d647 |
if (create) {
|
|
Packit |
01d647 |
if (io_->open() == 0) {
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "Exiv2::EpsImage:: Creating blank EPS image\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
IoCloser closer(*io_);
|
|
Packit |
01d647 |
if (io_->write(reinterpret_cast<const byte*>(epsBlank.data()), static_cast<long>(epsBlank.size())) != static_cast<long>(epsBlank.size())) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Failed to write blank EPS image.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
std::string EpsImage::mimeType() const
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
return "application/postscript";
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void EpsImage::setComment(const std::string& /*comment*/)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
throw Error(kerInvalidSettingForImage, "Image comment", "EPS");
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void EpsImage::readMetadata()
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "Exiv2::EpsImage::readMetadata: Reading EPS file " << io_->path() << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// read metadata
|
|
Packit |
01d647 |
readWriteEpsMetadata(*io_, xmpPacket_, nativePreviews_, /* write = */ false);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// decode XMP metadata
|
|
Packit |
01d647 |
if (xmpPacket_.size() > 0 && XmpParser::decode(xmpData_, xmpPacket_) > 1) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Failed to decode XMP metadata.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(kerFailedToReadImageData);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "Exiv2::EpsImage::readMetadata: Finished reading EPS file " << io_->path() << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
void EpsImage::writeMetadata()
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "Exiv2::EpsImage::writeMetadata: Writing EPS file " << io_->path() << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// encode XMP metadata if necessary
|
|
Packit |
01d647 |
if (!writeXmpFromPacket() && XmpParser::encode(xmpPacket_, xmpData_) > 1) {
|
|
Packit |
01d647 |
#ifndef SUPPRESS_WARNINGS
|
|
Packit |
01d647 |
EXV_WARNING << "Failed to encode XMP metadata.\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
throw Error(kerImageWriteFailed);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// write metadata
|
|
Packit |
01d647 |
readWriteEpsMetadata(*io_, xmpPacket_, nativePreviews_, /* write = */ true);
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
#ifdef DEBUG
|
|
Packit |
01d647 |
EXV_DEBUG << "Exiv2::EpsImage::writeMetadata: Finished writing EPS file " << io_->path() << "\n";
|
|
Packit |
01d647 |
#endif
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
// *************************************************************************
|
|
Packit |
01d647 |
// free functions
|
|
Packit |
01d647 |
Image::AutoPtr newEpsInstance(BasicIo::AutoPtr io, bool create)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
Image::AutoPtr image(new EpsImage(io, create));
|
|
Packit |
01d647 |
if (!image->good()) {
|
|
Packit |
01d647 |
image.reset();
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
return image;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
bool isEpsType(BasicIo& iIo, bool advance)
|
|
Packit |
01d647 |
{
|
|
Packit |
01d647 |
// read as many bytes as needed for the longest (DOS) EPS signature
|
|
Packit |
01d647 |
long bufSize = static_cast<long>(dosEpsSignature.size());
|
|
Packit |
01d647 |
for (size_t i = 0; i < (sizeof epsFirstLine) / (sizeof *epsFirstLine); i++) {
|
|
Packit |
01d647 |
if (bufSize < static_cast<long>(epsFirstLine[i].size())) {
|
|
Packit |
01d647 |
bufSize = static_cast<long>(epsFirstLine[i].size());
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
DataBuf buf = iIo.read(bufSize);
|
|
Packit |
01d647 |
if (iIo.error() || buf.size_ != bufSize) {
|
|
Packit |
01d647 |
return false;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
// check for all possible (DOS) EPS signatures
|
|
Packit |
01d647 |
bool matched = (memcmp(buf.pData_, dosEpsSignature.data(), dosEpsSignature.size()) == 0);
|
|
Packit |
01d647 |
for (size_t i = 0; !matched && i < (sizeof epsFirstLine) / (sizeof *epsFirstLine); i++) {
|
|
Packit |
01d647 |
matched = (memcmp(buf.pData_, epsFirstLine[i].data(), epsFirstLine[i].size()) == 0);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
// seek back if possible and requested
|
|
Packit |
01d647 |
if (!advance || !matched) {
|
|
Packit |
01d647 |
iIo.seek(-buf.size_, BasicIo::cur);
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
return matched;
|
|
Packit |
01d647 |
}
|
|
Packit |
01d647 |
|
|
Packit |
01d647 |
} // namespace Exiv2
|