mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-01-29 00:29:43 +01:00
DeviceN color space
This commit is contained in:
parent
31eae284c2
commit
3e345a768f
@ -243,7 +243,7 @@ QColor PDFAbstractColorSpace::getCheckedColor(const PDFColor& color) const
|
|||||||
{
|
{
|
||||||
if (getColorComponentCount() != color.size())
|
if (getColorComponentCount() != color.size())
|
||||||
{
|
{
|
||||||
throw PDFParserException(PDFTranslationContext::tr("Invalid number of color components. Expected number is %1, actual number is %2.").arg(getColorComponentCount(), color.size()));
|
throw PDFParserException(PDFTranslationContext::tr("Invalid number of color components. Expected number is %1, actual number is %2.").arg(static_cast<int>(getColorComponentCount()), static_cast<int>(color.size())));
|
||||||
}
|
}
|
||||||
|
|
||||||
return getColor(color);
|
return getColor(color);
|
||||||
@ -389,6 +389,11 @@ PDFColorSpacePointer PDFAbstractColorSpace::createColorSpaceImpl(const PDFDictio
|
|||||||
return PDFSeparationColorSpace::createSeparationColorSpace(colorSpaceDictionary, document, array, recursion);
|
return PDFSeparationColorSpace::createSeparationColorSpace(colorSpaceDictionary, document, array, recursion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (name == COLOR_SPACE_NAME_DEVICE_N && count >= 4)
|
||||||
|
{
|
||||||
|
return PDFDeviceNColorSpace::createDeviceNColorSpace(colorSpaceDictionary, document, array, recursion);
|
||||||
|
}
|
||||||
|
|
||||||
// Try to just load by standard way - we can have "standard" color space stored in array
|
// Try to just load by standard way - we can have "standard" color space stored in array
|
||||||
return createColorSpaceImpl(colorSpaceDictionary, document, colorSpaceIdentifier, recursion);
|
return createColorSpaceImpl(colorSpaceDictionary, document, colorSpaceIdentifier, recursion);
|
||||||
}
|
}
|
||||||
@ -985,6 +990,7 @@ QColor PDFPatternColorSpace::getDefaultColor() const
|
|||||||
|
|
||||||
QColor PDFPatternColorSpace::getColor(const PDFColor& color) const
|
QColor PDFPatternColorSpace::getColor(const PDFColor& color) const
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(color);
|
||||||
throw PDFParserException(PDFTranslationContext::tr("Pattern doesn't have defined uniform color."));
|
throw PDFParserException(PDFTranslationContext::tr("Pattern doesn't have defined uniform color."));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -993,4 +999,183 @@ size_t PDFPatternColorSpace::getColorComponentCount() const
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PDFDeviceNColorSpace::PDFDeviceNColorSpace(PDFDeviceNColorSpace::Type type,
|
||||||
|
PDFDeviceNColorSpace::Colorants&& colorants,
|
||||||
|
PDFColorSpacePointer alternateColorSpace,
|
||||||
|
PDFColorSpacePointer processColorSpace,
|
||||||
|
PDFFunctionPtr tintTransform,
|
||||||
|
std::vector<QByteArray>&& colorantsPrintingOrder,
|
||||||
|
std::vector<QByteArray> processColorSpaceComponents) :
|
||||||
|
m_type(type),
|
||||||
|
m_colorants(qMove(colorants)),
|
||||||
|
m_alternateColorSpace(qMove(alternateColorSpace)),
|
||||||
|
m_processColorSpace(qMove(processColorSpace)),
|
||||||
|
m_tintTransform(qMove(tintTransform)),
|
||||||
|
m_colorantsPrintingOrder(qMove(colorantsPrintingOrder)),
|
||||||
|
m_processColorSpaceComponents(qMove(processColorSpaceComponents))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor PDFDeviceNColorSpace::getDefaultColor() const
|
||||||
|
{
|
||||||
|
PDFColor color;
|
||||||
|
color.resize(getColorComponentCount());
|
||||||
|
return getColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor PDFDeviceNColorSpace::getColor(const PDFColor& color) const
|
||||||
|
{
|
||||||
|
// Input values
|
||||||
|
std::vector<double> inputColor(color.size(), 0.0);
|
||||||
|
for (size_t i = 0, count = inputColor.size(); i < count; ++i)
|
||||||
|
{
|
||||||
|
inputColor[i] = color[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output values
|
||||||
|
std::vector<double> outputColor;
|
||||||
|
outputColor.resize(m_alternateColorSpace->getColorComponentCount(), 0.0);
|
||||||
|
PDFFunction::FunctionResult result = m_tintTransform->apply(inputColor.data(), inputColor.data() + inputColor.size(), outputColor.data(), outputColor.data() + outputColor.size());
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
PDFColor color;
|
||||||
|
std::for_each(outputColor.cbegin(), outputColor.cend(), [&color](double value) { color.push_back(static_cast<float>(value)); });
|
||||||
|
return m_alternateColorSpace->getColor(color);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Return invalid color
|
||||||
|
return QColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PDFDeviceNColorSpace::getColorComponentCount() const
|
||||||
|
{
|
||||||
|
return m_colorants.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFColorSpacePointer PDFDeviceNColorSpace::createDeviceNColorSpace(const PDFDictionary* colorSpaceDictionary,
|
||||||
|
const PDFDocument* document,
|
||||||
|
const PDFArray* array,
|
||||||
|
int recursion)
|
||||||
|
{
|
||||||
|
Q_ASSERT(array->getCount() >= 4);
|
||||||
|
|
||||||
|
PDFDocumentDataLoaderDecorator loader(document);
|
||||||
|
std::vector<QByteArray> colorantNames = loader.readNameArray(array->getItem(1));
|
||||||
|
|
||||||
|
if (colorantNames.empty())
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid colorants for DeviceN color space."));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ColorantInfo> colorants;
|
||||||
|
colorants.resize(colorantNames.size());
|
||||||
|
for (size_t i = 0; i < colorantNames.size(); ++i)
|
||||||
|
{
|
||||||
|
colorants[i].name = qMove(colorantNames[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read alternate color space
|
||||||
|
PDFColorSpacePointer alternateColorSpace = PDFAbstractColorSpace::createColorSpaceImpl(colorSpaceDictionary, document, document->getObject(array->getItem(2)), recursion);
|
||||||
|
if (!alternateColorSpace)
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Can't determine alternate color space for DeviceN color space."));
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFFunctionPtr tintTransform = PDFFunction::createFunction(document, array->getItem(3));
|
||||||
|
if (!tintTransform)
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Can't determine tint transform for DeviceN color space."));
|
||||||
|
}
|
||||||
|
|
||||||
|
Type type = Type::DeviceN;
|
||||||
|
std::vector<QByteArray> colorantsPrintingOrder;
|
||||||
|
PDFColorSpacePointer processColorSpace;
|
||||||
|
std::vector<QByteArray> processColorSpaceComponents;
|
||||||
|
|
||||||
|
// Now, check, if we have attributes, and if yes, then read them
|
||||||
|
if (array->getCount() == 5)
|
||||||
|
{
|
||||||
|
const PDFObject& object = document->getObject(array->getItem(4));
|
||||||
|
if (object.isDictionary())
|
||||||
|
{
|
||||||
|
const PDFDictionary* attributesDictionary = object.getDictionary();
|
||||||
|
QByteArray subtype = loader.readNameFromDictionary(attributesDictionary, "Subtype");
|
||||||
|
if (subtype == "NChannel")
|
||||||
|
{
|
||||||
|
type = Type::NChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PDFObject& colorantsObject = document->getObject(attributesDictionary->get("Colorants"));
|
||||||
|
if (colorantsObject.isDictionary())
|
||||||
|
{
|
||||||
|
const PDFDictionary* colorantsDictionary = colorantsObject.getDictionary();
|
||||||
|
|
||||||
|
// Separation color spaces for each colorant
|
||||||
|
for (ColorantInfo& colorantInfo : colorants)
|
||||||
|
{
|
||||||
|
if (colorantsDictionary->hasKey(colorantInfo.name))
|
||||||
|
{
|
||||||
|
colorantInfo.separationColorSpace = PDFAbstractColorSpace::createColorSpaceImpl(colorSpaceDictionary, document, document->getObject(colorantsDictionary->get(colorantInfo.name)), recursion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PDFObject& mixingHints = document->getObject(attributesDictionary->get("MixingHints"));
|
||||||
|
if (mixingHints.isDictionary())
|
||||||
|
{
|
||||||
|
const PDFDictionary* mixingHintsDictionary = mixingHints.getDictionary();
|
||||||
|
|
||||||
|
// Printing order
|
||||||
|
colorantsPrintingOrder = loader.readNameArray(mixingHintsDictionary->get("PrintingOrder"));
|
||||||
|
|
||||||
|
// Solidities
|
||||||
|
const PDFObject& solidityObject = document->getObject(mixingHintsDictionary->get("Solidites"));
|
||||||
|
if (solidityObject.isDictionary())
|
||||||
|
{
|
||||||
|
const PDFDictionary* solidityDictionary = solidityObject.getDictionary();
|
||||||
|
const PDFReal defaultSolidity = loader.readNumberFromDictionary(solidityDictionary, "Default", 0.0);
|
||||||
|
for (ColorantInfo& colorantInfo : colorants)
|
||||||
|
{
|
||||||
|
colorantInfo.solidity = loader.readNumberFromDictionary(solidityDictionary, colorantInfo.name, defaultSolidity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dot gain
|
||||||
|
const PDFObject& dotGainObject = document->getObject(mixingHintsDictionary->get("DotGain"));
|
||||||
|
if (dotGainObject.isDictionary())
|
||||||
|
{
|
||||||
|
const PDFDictionary* dotGainDictionary = dotGainObject.getDictionary();
|
||||||
|
for (ColorantInfo& colorantInfo : colorants)
|
||||||
|
{
|
||||||
|
const PDFObject& dotGainFunctionObject = document->getObject(dotGainDictionary->get(colorantInfo.name));
|
||||||
|
if (!dotGainFunctionObject.isNull())
|
||||||
|
{
|
||||||
|
colorantInfo.dotGain = PDFFunction::createFunction(document, dotGainFunctionObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process
|
||||||
|
const PDFObject& processObject = document->getObject(attributesDictionary->get("Process"));
|
||||||
|
if (processObject.isDictionary())
|
||||||
|
{
|
||||||
|
const PDFDictionary* processDictionary = processObject.getDictionary();
|
||||||
|
const PDFObject& processColorSpaceObject = document->getObject(processDictionary->get("ColorSpace"));
|
||||||
|
if (!processColorSpaceObject.isNull())
|
||||||
|
{
|
||||||
|
processColorSpace = PDFAbstractColorSpace::createColorSpaceImpl(colorSpaceDictionary, document, processColorSpaceObject, recursion);
|
||||||
|
processColorSpaceComponents = loader.readNameArrayFromDictionary(processDictionary, "Components");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PDFColorSpacePointer(new PDFDeviceNColorSpace(type, qMove(colorants), qMove(alternateColorSpace), qMove(processColorSpace), qMove(tintTransform), qMove(colorantsPrintingOrder), qMove(processColorSpaceComponents)));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
@ -61,6 +61,7 @@ static constexpr const char* COLOR_SPACE_NAME_LAB = "Lab";
|
|||||||
static constexpr const char* COLOR_SPACE_NAME_ICCBASED = "ICCBased";
|
static constexpr const char* COLOR_SPACE_NAME_ICCBASED = "ICCBased";
|
||||||
static constexpr const char* COLOR_SPACE_NAME_INDEXED = "Indexed";
|
static constexpr const char* COLOR_SPACE_NAME_INDEXED = "Indexed";
|
||||||
static constexpr const char* COLOR_SPACE_NAME_SEPARATION = "Separation";
|
static constexpr const char* COLOR_SPACE_NAME_SEPARATION = "Separation";
|
||||||
|
static constexpr const char* COLOR_SPACE_NAME_DEVICE_N = "DeviceN";
|
||||||
static constexpr const char* COLOR_SPACE_NAME_PATTERN = "Pattern";
|
static constexpr const char* COLOR_SPACE_NAME_PATTERN = "Pattern";
|
||||||
|
|
||||||
static constexpr const char* CAL_WHITE_POINT = "WhitePoint";
|
static constexpr const char* CAL_WHITE_POINT = "WhitePoint";
|
||||||
@ -535,6 +536,59 @@ private:
|
|||||||
PDFFunctionPtr m_tintTransform;
|
PDFFunctionPtr m_tintTransform;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PDFDeviceNColorSpace : public PDFAbstractColorSpace
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum class Type
|
||||||
|
{
|
||||||
|
DeviceN,
|
||||||
|
NChannel
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ColorantInfo
|
||||||
|
{
|
||||||
|
QByteArray name;
|
||||||
|
PDFColorSpacePointer separationColorSpace;
|
||||||
|
PDFReal solidity = 0.0;
|
||||||
|
PDFFunctionPtr dotGain;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Colorants = std::vector<ColorantInfo>;
|
||||||
|
|
||||||
|
explicit PDFDeviceNColorSpace(Type type,
|
||||||
|
Colorants&& colorants,
|
||||||
|
PDFColorSpacePointer alternateColorSpace,
|
||||||
|
PDFColorSpacePointer processColorSpace,
|
||||||
|
PDFFunctionPtr tintTransform,
|
||||||
|
std::vector<QByteArray>&& colorantsPrintingOrder,
|
||||||
|
std::vector<QByteArray> processColorSpaceComponents);
|
||||||
|
virtual ~PDFDeviceNColorSpace() = default;
|
||||||
|
|
||||||
|
virtual QColor getDefaultColor() const override;
|
||||||
|
virtual QColor getColor(const PDFColor& color) const override;
|
||||||
|
virtual size_t getColorComponentCount() const override;
|
||||||
|
|
||||||
|
/// Returns type of DeviceN color space
|
||||||
|
Type getType() const { return m_type; }
|
||||||
|
|
||||||
|
/// Creates DeviceN color space from provided values.
|
||||||
|
/// \param colorSpaceDictionary Color space dictionary
|
||||||
|
/// \param document Document
|
||||||
|
/// \param array Array with DeviceN color space definition
|
||||||
|
/// \param recursion Recursion guard
|
||||||
|
static PDFColorSpacePointer createDeviceNColorSpace(const PDFDictionary* colorSpaceDictionary, const PDFDocument* document, const PDFArray* array, int recursion);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type m_type;
|
||||||
|
Colorants m_colorants;
|
||||||
|
PDFColorSpacePointer m_alternateColorSpace;
|
||||||
|
PDFColorSpacePointer m_processColorSpace;
|
||||||
|
PDFFunctionPtr m_tintTransform;
|
||||||
|
std::vector<QByteArray> m_colorantsPrintingOrder;
|
||||||
|
std::vector<QByteArray> m_processColorSpaceComponents;
|
||||||
|
};
|
||||||
|
|
||||||
class PDFPatternColorSpace : public PDFAbstractColorSpace
|
class PDFPatternColorSpace : public PDFAbstractColorSpace
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -551,8 +605,6 @@ private:
|
|||||||
std::shared_ptr<PDFPattern> m_pattern;
|
std::shared_ptr<PDFPattern> m_pattern;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Implement DeviceN color space
|
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
|
||||||
#endif // PDFCOLORSPACES_H
|
#endif // PDFCOLORSPACES_H
|
||||||
|
@ -342,6 +342,16 @@ PDFReal PDFDocumentDataLoaderDecorator::readNumberFromDictionary(const PDFDictio
|
|||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PDFReal PDFDocumentDataLoaderDecorator::readNumberFromDictionary(const PDFDictionary* dictionary, const QByteArray& key, PDFReal defaultValue) const
|
||||||
|
{
|
||||||
|
if (dictionary->hasKey(key))
|
||||||
|
{
|
||||||
|
return readNumber(dictionary->get(key), defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
PDFInteger PDFDocumentDataLoaderDecorator::readIntegerFromDictionary(const PDFDictionary* dictionary, const char* key, PDFInteger defaultValue) const
|
PDFInteger PDFDocumentDataLoaderDecorator::readIntegerFromDictionary(const PDFDictionary* dictionary, const char* key, PDFInteger defaultValue) const
|
||||||
{
|
{
|
||||||
if (dictionary->hasKey(key))
|
if (dictionary->hasKey(key))
|
||||||
|
@ -214,6 +214,12 @@ public:
|
|||||||
/// \param defaultValue Default value
|
/// \param defaultValue Default value
|
||||||
PDFReal readNumberFromDictionary(const PDFDictionary* dictionary, const char* key, PDFReal defaultValue) const;
|
PDFReal readNumberFromDictionary(const PDFDictionary* dictionary, const char* key, PDFReal defaultValue) const;
|
||||||
|
|
||||||
|
/// Reads number from dictionary. If dictionary entry doesn't exist, or error occurs, default value is returned.
|
||||||
|
/// \param dictionary Dictionary containing desired data
|
||||||
|
/// \param key Entry key
|
||||||
|
/// \param defaultValue Default value
|
||||||
|
PDFReal readNumberFromDictionary(const PDFDictionary* dictionary, const QByteArray& key, PDFReal defaultValue) const;
|
||||||
|
|
||||||
/// Reads integer from dictionary. If dictionary entry doesn't exist, or error occurs, default value is returned.
|
/// Reads integer from dictionary. If dictionary entry doesn't exist, or error occurs, default value is returned.
|
||||||
/// \param dictionary Dictionary containing desired data
|
/// \param dictionary Dictionary containing desired data
|
||||||
/// \param key Entry key
|
/// \param key Entry key
|
||||||
|
@ -598,28 +598,12 @@ PDFFunction::FunctionResult PDFStitchingFunction::apply(const_iterator x_1,
|
|||||||
|
|
||||||
// First search for partial function, which defines our range. Use algorithm
|
// First search for partial function, which defines our range. Use algorithm
|
||||||
// similar to the std::lower_bound.
|
// similar to the std::lower_bound.
|
||||||
size_t count = m_partialFunctions.size();
|
auto it = std::lower_bound(m_partialFunctions.cbegin(), m_partialFunctions.cend(), x, [](const auto& partialFunction, PDFReal value) { return partialFunction.bound1 < value; });
|
||||||
size_t functionIndex = 0;
|
if (it == m_partialFunctions.cend())
|
||||||
while (count > 0)
|
|
||||||
{
|
{
|
||||||
const size_t step = count / 2;
|
--it;
|
||||||
const size_t current = functionIndex + step;
|
|
||||||
|
|
||||||
if (m_partialFunctions[current].bound1 < x)
|
|
||||||
{
|
|
||||||
functionIndex = current + 1;
|
|
||||||
count = count - functionIndex;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
count = current;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (functionIndex == m_partialFunctions.size())
|
const PartialFunction& function = *it;
|
||||||
{
|
|
||||||
--functionIndex;
|
|
||||||
}
|
|
||||||
const PartialFunction& function = m_partialFunctions[functionIndex];
|
|
||||||
|
|
||||||
// Encode the value into the input range of the function
|
// Encode the value into the input range of the function
|
||||||
const PDFReal xEncoded = interpolate(x, function.bound0, function.bound1, function.encode0, function.encode1);
|
const PDFReal xEncoded = interpolate(x, function.bound0, function.bound1, function.encode0, function.encode1);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user