#include #include #include #include #include "MemoryUtils.h" #include "Object.h" #include "Dict.h" #include "Stream.h" #include "Function.h" namespace PdfReader { //------------------------------------------------------------------------ // Function //------------------------------------------------------------------------ Function::Function() { } Function::~Function() { } Function *Function::Parse(Object *pFuncObject) { Dict *pDict; // Функция задается либо как словарь, либо поток, либо как тождественная функция if (pFuncObject->IsStream()) { pDict = pFuncObject->StreamGetDict(); } else if (pFuncObject->IsDict()) { pDict = pFuncObject->GetDict(); } else if (pFuncObject->IsName("Identity")) { return new IdentityFunction(); } else { // TO DO: Error "Expected function dictionary or stream" return NULL; } Object oTemp; if (!pDict->Search("FunctionType", &oTemp)->IsInt()) { // TO DO: Error "Function type is missing or wrong type" oTemp.Free(); return NULL; } int nFuncType = oTemp.GetInt(); oTemp.Free(); Function *pFunc = NULL; if (0 == nFuncType) // Sampled function { pFunc = new SampledFunction(pFuncObject, pDict); } else if (2 == nFuncType) // Exponential interpolation function { pFunc = new ExponentialFunction(pFuncObject, pDict); } else if (3 == nFuncType) // Stitching function { pFunc = new StitchingFunction(pFuncObject, pDict); } else if (4 == nFuncType) // PostScript calculator function { pFunc = new PostScriptFunction(pFuncObject, pDict); } else { // TO DO: Error "Unimplemented function type" return NULL; } if (!pFunc->IsValid()) { delete pFunc; return NULL; } return pFunc; } bool Function::Initialize(Dict *pDict) { Object oArray; // Domain (Всегда должен присутствовать) if (!pDict->Search("Domain", &oArray)->IsArray()) { // TO DO: Error "Function is missing domain" oArray.Free(); return false; } m_nInputDim = oArray.ArrayGetLength() / 2; if (m_nInputDim > funcMaxInputs) { // TO DO: Error "Functions with more than funcMaxInputs inputs are unsupported" oArray.Free(); return false; } for (int nIndex = 0; nIndex < m_nInputDim; ++nIndex) { Object oTemp; oArray.ArrayGet(2 * nIndex, &oTemp); if (!oTemp.IsNum()) { // TO DO: Error "Illegal value in function domain array" oTemp.Free(); oArray.Free(); return false; } m_arrDomain[nIndex][0] = oTemp.GetNum(); oTemp.Free(); oArray.ArrayGet(2 * nIndex + 1, &oTemp); if (!oTemp.IsNum()) { // TO DO: Error "Illegal value in function domain array" oTemp.Free(); oArray.Free(); return false; } m_arrDomain[nIndex][1] = oTemp.GetNum(); oTemp.Free(); } oArray.Free(); // Range (Этого поля может и не быть) m_bHasRange = false; m_nOutputDim = 0; if (pDict->Search("Range", &oArray)->IsArray()) { m_bHasRange = true; m_nOutputDim = oArray.ArrayGetLength() / 2; if (m_nOutputDim > funcMaxOutputs) { // TO DO: Error "Functions with more than funcMaxOutputs outputs are unsupported" oArray.Free(); return false; } for (int nIndex = 0; nIndex < m_nOutputDim; ++nIndex) { Object oTemp; oArray.ArrayGet(2 * nIndex, &oTemp); if (!oTemp.IsNum()) { // TO DO: Error "Illegal value in function range array" oTemp.Free(); oArray.Free(); return false; } m_arrRange[nIndex][0] = oTemp.GetNum(); oTemp.Free(); oArray.ArrayGet(2 * nIndex + 1, &oTemp); if (!oTemp.IsNum()) { // TO DO: Error "Illegal value in function range array" oTemp.Free(); oArray.Free(); return false; } m_arrRange[nIndex][1] = oTemp.GetNum(); oTemp.Free(); } } oArray.Free(); return true; } //------------------------------------------------------------------------ // IdentityFunction //------------------------------------------------------------------------ IdentityFunction::IdentityFunction() { // Заполняем произвольными значениями m_nInputDim = funcMaxInputs; m_nOutputDim = funcMaxOutputs; for (int nIndex = 0; nIndex < funcMaxInputs; ++nIndex) { m_arrDomain[nIndex][0] = 0; m_arrDomain[nIndex][1] = 1; } m_bHasRange = false; } IdentityFunction::~IdentityFunction() { } void IdentityFunction::Transform(double *pInput, double *pOutput) { for (int nIndex = 0; nIndex < funcMaxOutputs; ++nIndex) { pOutput[nIndex] = pInput[nIndex]; // тождественная же функция :) } } //------------------------------------------------------------------------ // SampledFunction //------------------------------------------------------------------------ SampledFunction::SampledFunction(Object *pFuncObject, Dict *pDict) { m_pSamples = NULL; m_pBuffer = NULL; m_bValid = false; if (!Initialize(pDict)) { return; } if (!m_bHasRange) { // TO DO: Error "Type 0 function is missing range" return; } if (m_nInputDim > sampledFuncMaxInputs) { // TO DO: Error "Sampled functions with more than sampledFuncMaxInputs inputs are unsupported" return; } m_pBuffer = (double *)MemUtilsMallocArray(1 << m_nInputDim, sizeof(double)); if (!pFuncObject->IsStream()) { // TO DO: Error "Type 0 function isn't a stream" return; } Stream *pStream = pFuncObject->GetStream(); // Size Object oArray; if (!pDict->Search("Size", &oArray)->IsArray() || oArray.ArrayGetLength() != m_nInputDim) { // TO DO: Error "Function has missing or invalid size array" oArray.Free(); return; } for (int nIndex = 0; nIndex < m_nInputDim; ++nIndex) { Object oTemp; oArray.ArrayGet(nIndex, &oTemp); if (!oTemp.IsInt()) { // TO DO: Error "Illegal value in function size array" oTemp.Free(); oArray.Free(); return; } m_arrSize[nIndex] = oTemp.GetInt(); oTemp.Free(); } oArray.Free(); m_arrIndexMult[0] = m_nOutputDim; for (int nIndex = 1; nIndex < m_nInputDim; ++nIndex) { m_arrIndexMult[nIndex] = m_arrIndexMult[nIndex - 1] * m_arrSize[nIndex - 1]; } // BitsPerSample if (!pDict->Search("BitsPerSample", &oArray)->IsInt()) { // TO DO: Error "Function has missing or invalid BitsPerSample" oArray.Free(); return; } int nSampleBits = oArray.GetInt(); double dSampleMult = 1.0 / (pow(2.0, (double)nSampleBits) - 1); oArray.Free(); // TO DO: Сделать чтение поля Order, и реализовать функцию Transform // Encode if (pDict->Search("Encode", &oArray)->IsArray() && oArray.ArrayGetLength() == 2 * m_nInputDim) { for (int nIndex = 0; nIndex < m_nInputDim; ++nIndex) { Object oTemp; oArray.ArrayGet(2 * nIndex, &oTemp); if (!oTemp.IsNum()) { // TO DO: Error "Illegal value in function encode array" oTemp.Free(); oArray.Free(); return; } m_arrEncoder[nIndex][0] = oTemp.GetNum(); oTemp.Free(); oArray.ArrayGet(2 * nIndex + 1, &oTemp); if (!oTemp.IsNum()) { // TO DO: Error "Illegal value in function encode array" oTemp.Free(); oArray.Free(); return; } m_arrEncoder[nIndex][1] = oTemp.GetNum(); oTemp.Free(); } } else { for (int nIndex = 0; nIndex < m_nInputDim; ++nIndex) { m_arrEncoder[nIndex][0] = 0; m_arrEncoder[nIndex][1] = m_arrSize[nIndex] - 1; } } oArray.Free(); for (int nIndex = 0; nIndex < m_nInputDim; ++nIndex) { m_arrInputMult[nIndex] = (m_arrEncoder[nIndex][1] - m_arrEncoder[nIndex][0]) / (m_arrDomain[nIndex][1] - m_arrDomain[nIndex][0]); } // Decode if (pDict->Search("Decode", &oArray)->IsArray() && oArray.ArrayGetLength() == 2 * m_nOutputDim) { for (int nIndex = 0; nIndex < m_nOutputDim; ++nIndex) { Object oTemp; oArray.ArrayGet(2 * nIndex, &oTemp); if (!oTemp.IsNum()) { // TO DO: Error "Illegal value in function decode array" oTemp.Free(); oArray.Free(); return; } m_arrDecoder[nIndex][0] = oTemp.GetNum(); oTemp.Free(); oArray.ArrayGet(2 * nIndex + 1, &oTemp); if (!oTemp.IsNum()) { // TO DO: Error "Illegal value in function decode array" oTemp.Free(); oArray.Free(); return; } m_arrDecoder[nIndex][1] = oTemp.GetNum(); oTemp.Free(); } } else { for (int nIndex = 0; nIndex < m_nOutputDim; ++nIndex) { m_arrDecoder[nIndex][0] = m_arrRange[nIndex][0]; m_arrDecoder[nIndex][1] = m_arrRange[nIndex][1]; } } oArray.Free(); // Samples m_nSamplesCount = m_nOutputDim; for (int nIndex = 0; nIndex < m_nInputDim; ++nIndex) m_nSamplesCount *= m_arrSize[nIndex]; m_pSamples = (double *)MemUtilsMallocArray(m_nSamplesCount, sizeof(double)); unsigned int unBuf = 0; int nBitsCount = 0; unsigned int unBitMask = (1 << nSampleBits) - 1; pStream->Reset(); for (int nIndex = 0; nIndex < m_nSamplesCount; ++nIndex) { unsigned int unValue = 0; if (nSampleBits == 8) { unValue = pStream->GetChar(); } else if (nSampleBits == 16) { unValue = pStream->GetChar(); unValue = (unValue << 8) + pStream->GetChar(); } else if (nSampleBits == 32) { unValue = pStream->GetChar(); unValue = (unValue << 8) + pStream->GetChar(); unValue = (unValue << 8) + pStream->GetChar(); unValue = (unValue << 8) + pStream->GetChar(); } else { while (nBitsCount < nSampleBits) { unBuf = (unBuf << 8) | (pStream->GetChar() & 0xff); nBitsCount += 8; } unValue = (unBuf >> (nBitsCount - nSampleBits)) & unBitMask; nBitsCount -= nSampleBits; } m_pSamples[nIndex] = (double)unValue * dSampleMult; } pStream->Close(); m_bValid = true; return; } SampledFunction::~SampledFunction() { MemUtilsFree(m_pSamples); MemUtilsFree(m_pBuffer); } SampledFunction::SampledFunction(SampledFunction *pFunc) { memcpy(this, pFunc, sizeof(SampledFunction)); m_pSamples = (double *)MemUtilsMallocArray(m_nSamplesCount, sizeof(double)); memcpy(m_pSamples, pFunc->m_pSamples, m_nSamplesCount * sizeof(double)); m_pBuffer = (double *)MemUtilsMallocArray(1 << m_nInputDim, sizeof(double)); } void SampledFunction::Transform(double *pInput, double *pOutput) { int arrE[funcMaxInputs][2]; double arrEFrac0[funcMaxInputs]; double arrEFrac1[funcMaxInputs]; for (int nIndex = 0; nIndex < m_nInputDim; ++nIndex) { double dValue = (pInput[nIndex] - m_arrDomain[nIndex][0]) * m_arrInputMult[nIndex] + m_arrEncoder[nIndex][0]; if (dValue < 0) { dValue = 0; } else if (dValue > m_arrSize[nIndex] - 1) { dValue = m_arrSize[nIndex] - 1; } arrE[nIndex][0] = (int)dValue; if ((arrE[nIndex][1] = arrE[nIndex][0] + 1) >= m_arrSize[nIndex]) { arrE[nIndex][1] = arrE[nIndex][0]; } arrEFrac1[nIndex] = dValue - arrE[nIndex][0]; arrEFrac0[nIndex] = 1 - arrEFrac1[nIndex]; } for (int nI = 0; nI < m_nOutputDim; ++nI) { for (int nJ = 0; nJ < (1 << m_nInputDim); ++nJ) { int nIdx = nI; for (int nK = 0, t = nJ; nK < m_nInputDim; ++nK, t >>= 1) { nIdx += m_arrIndexMult[nK] * (arrE[nK][t & 1]); } m_pBuffer[nJ] = m_pSamples[nIdx]; } int nT = 0; for (int nJ = 0, nT = (1 << m_nInputDim); nJ < m_nInputDim; ++nJ, nT >>= 1) { for (int nK = 0; nK < nT; nK += 2) { m_pBuffer[nK >> 1] = arrEFrac0[nJ] * m_pBuffer[nK] + arrEFrac1[nJ] * m_pBuffer[nK + 1]; } } pOutput[nI] = m_pBuffer[0] * (m_arrDecoder[nI][1] - m_arrDecoder[nI][0]) + m_arrDecoder[nI][0]; if (pOutput[nI] < m_arrRange[nI][0]) { pOutput[nI] = m_arrRange[nI][0]; } else if (pOutput[nI] > m_arrRange[nI][1]) { pOutput[nI] = m_arrRange[nI][1]; } } } //------------------------------------------------------------------------ // ExponentialFunction //------------------------------------------------------------------------ ExponentialFunction::ExponentialFunction(Object *pFuncObject, Dict *pDict) { m_bValid = false; if (!Initialize(pDict)) { return; } if (m_nInputDim != 1) { // TO DO: Error "Exponential function with more than one input" return; } // C0 Object oArray; if (pDict->Search("C0", &oArray)->IsArray()) { if (m_bHasRange && oArray.ArrayGetLength() != m_nOutputDim) { // TO DO: Error "Function's C0 array is wrong length" oArray.Free(); return; } m_nOutputDim = oArray.ArrayGetLength(); for (int nIndex = 0; nIndex < m_nOutputDim; ++nIndex) { Object oTemp; oArray.ArrayGet(nIndex, &oTemp); if (!oTemp.IsNum()) { // TO DO: Error "Illegal value in function C0 array" oArray.Free(); oTemp.Free(); return; } m_arrC0[nIndex] = oTemp.GetNum(); oTemp.Free(); } } else { if (m_bHasRange && m_nOutputDim != 1) { // TO DO: Error "Function's C0 array is wrong length" oArray.Free(); return; } m_nOutputDim = 1; m_arrC0[0] = 0; } oArray.Free(); // C1 if (pDict->Search("C1", &oArray)->IsArray()) { if (oArray.ArrayGetLength() != m_nOutputDim) { // TO DO: Error "Function's C1 array is wrong length" oArray.Free(); return; } for (int nIndex = 0; nIndex < m_nOutputDim; ++nIndex) { Object oTemp; oArray.ArrayGet(nIndex, &oTemp); if (!oTemp.IsNum()) { // TO DO: Error "Illegal value in function C1 array" oTemp.Free(); oArray.Free(); return; } m_arrC1[nIndex] = oTemp.GetNum(); oTemp.Free(); } } else { if (m_nOutputDim != 1) { // TO DO: Error "Function's C1 array is wrong length" oArray.Free(); return; } m_arrC1[0] = 1; } oArray.Free(); // N (exponent) if (!pDict->Search("N", &oArray)->IsNum()) { // TO DO: Error "Function has missing or invalid N" oArray.Free(); return; } m_dN = oArray.GetNum(); oArray.Free(); m_bValid = true; return; } ExponentialFunction::~ExponentialFunction() { } ExponentialFunction::ExponentialFunction(ExponentialFunction *pFunc) { memcpy(this, pFunc, sizeof(ExponentialFunction)); } void ExponentialFunction::Transform(double *pInput, double *pOutput) { double dX = 0; if (pInput[0] < m_arrDomain[0][0]) { dX = m_arrDomain[0][0]; } else if (pInput[0] > m_arrDomain[0][1]) { dX = m_arrDomain[0][1]; } else { dX = pInput[0]; } for (int nIndex = 0; nIndex < m_nOutputDim; ++nIndex) { pOutput[nIndex] = m_arrC0[nIndex] + pow(dX, m_dN) * (m_arrC1[nIndex] - m_arrC0[nIndex]); if (m_bHasRange) { if (pOutput[nIndex] < m_arrRange[nIndex][0]) { pOutput[nIndex] = m_arrRange[nIndex][0]; } else if (pOutput[nIndex] > m_arrRange[nIndex][1]) { pOutput[nIndex] = m_arrRange[nIndex][1]; } } } return; } //------------------------------------------------------------------------ // StitchingFunction //------------------------------------------------------------------------ StitchingFunction::StitchingFunction(Object *pFuncObject, Dict *pDict) { m_bValid = false; m_ppFuncs = NULL; m_arrBounds = NULL; m_arrEncode = NULL; m_arrScale = NULL; if (!Initialize(pDict)) { return; } if (m_nInputDim != 1) { // TO DO: Error "Stitching function with more than one input" return; } // Functions Object oArray; if (!pDict->Search("Functions", &oArray)->IsArray()) { // TO DO: Error "Missing 'Functions' entry in stitching function" oArray.Free(); return; } m_nCount = oArray.ArrayGetLength(); m_ppFuncs = (Function**)MemUtilsMallocArray(m_nCount, sizeof(Function *)); m_arrBounds = (double *)MemUtilsMallocArray(m_nCount + 1, sizeof(double)); m_arrEncode = (double *)MemUtilsMallocArray(2 * m_nCount, sizeof(double)); m_arrScale = (double *)MemUtilsMallocArray(m_nCount, sizeof(double)); for (int nIndex = 0; nIndex < m_nCount; ++nIndex) { m_ppFuncs[nIndex] = NULL; } for (int nIndex = 0; nIndex < m_nCount; ++nIndex) { Object oTemp; if (!(m_ppFuncs[nIndex] = Function::Parse(oArray.ArrayGet(nIndex, &oTemp)))) { oTemp.Free(); oArray.Free(); return; } if (nIndex > 0 && (m_ppFuncs[nIndex]->GetInputSize() != 1 || m_ppFuncs[nIndex]->GetOutputSize() != m_ppFuncs[0]->GetOutputSize())) { // TO DO: Error "Incompatible subfunctions in stitching function" oTemp.Free(); oArray.Free(); return; } oTemp.Free(); } oArray.Free(); // Bounds if (!pDict->Search("Bounds", &oArray)->IsArray() || oArray.ArrayGetLength() != m_nCount - 1) { // TO DO: Error "Missing or invalid 'Bounds' entry in stitching function" oArray.Free(); return; } m_arrBounds[0] = m_arrDomain[0][0]; for (int nIndex = 1; nIndex < m_nCount; ++nIndex) { Object oTemp; if (!oArray.ArrayGet(nIndex - 1, &oTemp)->IsNum()) { // TO DO: Error "Invalid type in 'Bounds' array in stitching function" oTemp.Free(); oArray.Free(); return; } m_arrBounds[nIndex] = oTemp.GetNum(); oTemp.Free(); } m_arrBounds[m_nCount] = m_arrDomain[0][1]; oArray.Free(); // Encode if (!pDict->Search("Encode", &oArray)->IsArray() || oArray.ArrayGetLength() != 2 * m_nCount) { // TO DO: Error "Missing or invalid 'Encode' entry in stitching function" oArray.Free(); return; } for (int nIndex = 0; nIndex < 2 * m_nCount; ++nIndex) { Object oTemp; if (!oArray.ArrayGet(nIndex, &oTemp)->IsNum()) { // TO DO: Error "Invalid type in 'Encode' array in stitching function" oTemp.Free(); oArray.Free(); return; } m_arrEncode[nIndex] = oTemp.GetNum(); oTemp.Free(); } oArray.Free(); for (int nIndex = 0; nIndex < m_nCount; ++nIndex) { if (m_arrBounds[nIndex] == m_arrBounds[nIndex + 1]) { // Чтобы избежать деление на 0, в данном случае функция с номером nIndex не будет использоваться m_arrScale[nIndex] = 0; } else { m_arrScale[nIndex] = (m_arrEncode[2 * nIndex + 1] - m_arrEncode[2 * nIndex]) / (m_arrBounds[nIndex + 1] - m_arrBounds[nIndex]); } } m_bValid = true; return; } StitchingFunction::StitchingFunction(StitchingFunction *pFunc) { m_nCount = pFunc->m_nCount; m_ppFuncs = (Function **)MemUtilsMallocArray(m_nCount, sizeof(Function *)); for (int nIndex = 0; nIndex < m_nCount; ++nIndex) { m_ppFuncs[nIndex] = pFunc->m_ppFuncs[nIndex]->Copy(); } m_arrBounds = (double *)MemUtilsMallocArray(m_nCount + 1, sizeof(double)); memcpy(m_arrBounds, pFunc->m_arrBounds, (m_nCount + 1) * sizeof(double)); m_arrEncode = (double *)MemUtilsMallocArray(2 * m_nCount, sizeof(double)); memcpy(m_arrEncode, pFunc->m_arrEncode, 2 * m_nCount * sizeof(double)); m_arrScale = (double *)MemUtilsMallocArray(m_nCount, sizeof(double)); memcpy(m_arrScale, pFunc->m_arrScale, m_nCount * sizeof(double)); m_bValid = true; } StitchingFunction::~StitchingFunction() { if (m_ppFuncs) { for (int nIndex = 0; nIndex < m_nCount; ++nIndex) { delete m_ppFuncs[nIndex]; } } MemUtilsFree(m_ppFuncs); MemUtilsFree(m_arrBounds); MemUtilsFree(m_arrEncode); MemUtilsFree(m_arrScale); } void StitchingFunction::Transform(double *pInput, double *pOutput) { double dX; if (pInput[0] < m_arrDomain[0][0]) { dX = m_arrDomain[0][0]; } else if (pInput[0] > m_arrDomain[0][1]) { dX = m_arrDomain[0][1]; } else { dX = pInput[0]; } int nIndex = 0; for (nIndex = 0; nIndex < m_nCount - 1; ++nIndex) { if (dX < m_arrBounds[nIndex + 1]) { break; } } dX = m_arrEncode[2 * nIndex] + (dX - m_arrBounds[nIndex]) * m_arrScale[nIndex]; m_ppFuncs[nIndex]->Transform(&dX, pOutput); } //------------------------------------------------------------------------ // PostScriptFunction //------------------------------------------------------------------------ enum PSOperations { psOperationAbs, psOperationAdd, psOperationAnd, psOperationAtan, psOperationBitshift, psOperationCeiling, psOperationCopy, psOperationCos, psOperationCvi, psOperationCvr, psOperationDiv, psOperationDup, psOperationEq, psOperationExch, psOperationExp, psOperationFalse, psOperationFloor, psOperationGe, psOperationGt, psOperationIdiv, psOperationIndex, psOperationLe, psOperationLn, psOperationLog, psOperationLt, psOperationMod, psOperationMul, psOperationNe, psOperationNeg, psOperationNot, psOperationOr, psOperationPop, psOperationRoll, psOperationRound, psOperationSin, psOperationSqrt, psOperationSub, psOperationTrue, psOperationTruncate, psOperationXor, psOperationIf, psOperationIfelse, psOperationReturn }; // Список идет в алфавитном порядке. Номер элемента здесь, соответствует // операции в перечисляемом типе PSOperations char *c_sPSOperationNames[] = { "abs", "add", "and", "atan", "bitshift", "ceiling", "copy", "cos", "cvi", "cvr", "div", "dup", "eq", "exch", "exp", "false", "floor", "ge", "gt", "idiv", "index", "le", "ln", "log", "lt", "mod", "mul", "ne", "neg", "not", "or", "pop", "roll", "round", "sin", "sqrt", "sub", "true", "truncate", "xor" }; #define PSOperationsCount (sizeof(c_sPSOperationNames) / sizeof(char *)) enum PSObjectType { psBool, psInt, psReal, psOperator, psBlock }; // В потоке, операторы 'if'/'ifelse' занимают три слота. // // +---------------------------------+ // | psOperator: If / Ifelse | // +---------------------------------+ // | psBlock: указатель = | // +---------------------------------+ // | psBlock: указатель = | // +---------------------------------+ // | if | // | ... | // | psOperator: Return | // +---------------------------------+ // | else | // | ... | // | psOperator: Return | // +---------------------------------+ // | ... | // // Если оператор = 'if', указатель все задан, но не используется struct PSObject { PSObjectType eType; union { bool uBool; // boolean (stack only) int uInt; // integer (stack and code) double uReal; // real (stack and code) PSOperations uOperation; // operator (code only) int uBlock; // if/ifelse block pointer (code only) }; }; #define psStackSize 100 class PSStack { public: PSStack() { m_nStackSize = psStackSize; } void PushBool(bool bBool) { if (CheckOverflow()) { m_arrStack[--m_nStackSize].eType = psBool; m_arrStack[m_nStackSize].uBool = bBool; } } void PushInt(int nInteger) { if (CheckOverflow()) { m_arrStack[--m_nStackSize].eType = psInt; m_arrStack[m_nStackSize].uInt = nInteger; } } void PushReal(double dReal) { if (CheckOverflow()) { m_arrStack[--m_nStackSize].eType = psReal; m_arrStack[m_nStackSize].uReal = dReal; } } bool PopBool() { if (CheckUnderflow() && CheckType(psBool, psBool)) { return m_arrStack[m_nStackSize++].uBool; } return false; } int PopInt() { if (CheckUnderflow() && CheckType(psInt, psInt)) { return m_arrStack[m_nStackSize++].uInt; } return 0; } double PopNum() { if (CheckUnderflow() && CheckType(psInt, psReal)) { double dReturn = (m_arrStack[m_nStackSize].eType == psInt) ? (double)m_arrStack[m_nStackSize].uInt : m_arrStack[m_nStackSize].uReal; ++m_nStackSize; return dReturn; } return 0; } bool Empty() { return m_nStackSize == psStackSize; } bool TopIsInt() { return m_nStackSize < psStackSize && m_arrStack[m_nStackSize].eType == psInt; } bool TopTwoAreInts() { return m_nStackSize < psStackSize - 1 && m_arrStack[m_nStackSize].eType == psInt && m_arrStack[m_nStackSize + 1].eType == psInt; } bool TopIsReal() { return m_nStackSize < psStackSize && m_arrStack[m_nStackSize].eType == psReal; } bool TopTwoAreNums() { return m_nStackSize < psStackSize - 1 && (m_arrStack[m_nStackSize].eType == psInt || m_arrStack[m_nStackSize].eType == psReal) && (m_arrStack[m_nStackSize + 1].eType == psInt || m_arrStack[m_nStackSize + 1].eType == psReal); } void Copy(int nCount) { if (m_nStackSize + nCount > psStackSize) { // TO DO: Error "Stack underflow in PostScript function" return; } if (!CheckOverflow(nCount)) { return; } for (int nIndex = m_nStackSize + nCount - 1; nIndex >= m_nStackSize; --nIndex) { m_arrStack[nIndex - nCount] = m_arrStack[nIndex]; } m_nStackSize -= nCount; } void Roll(int n, int j) { int i, k; if (j >= 0) { j %= n; } else { j = -j % n; if (j != 0) { j = n - j; } } if (n <= 0 || j == 0) { return; } for (i = 0; i < j; ++i) { PSObject oPSObject = m_arrStack[m_nStackSize]; for (k = m_nStackSize; k < m_nStackSize + n - 1; ++k) { m_arrStack[k] = m_arrStack[k + 1]; } m_arrStack[m_nStackSize + n - 1] = oPSObject; } } void Index(int nIndex) { if (!CheckOverflow()) { return; } --m_nStackSize; m_arrStack[m_nStackSize] = m_arrStack[m_nStackSize + 1 + nIndex]; } void Pop() { if (!CheckUnderflow()) { return; } ++m_nStackSize; } private: bool CheckOverflow(int nCount = 1) { if (m_nStackSize - nCount < 0) { // TO DO: Error "Stack overflow in PostScript function" return false; } return true; } bool CheckUnderflow() { if (m_nStackSize == psStackSize) { // TO DO: Error "Stack underflow in PostScript function" return false; } return true; } bool CheckType(PSObjectType eType1, PSObjectType eType2) { if (m_arrStack[m_nStackSize].eType != eType1 && m_arrStack[m_nStackSize].eType != eType2) { // TO DO: Error "Type mismatch in PostScript function" return false; } return true; } private: PSObject m_arrStack[psStackSize]; int m_nStackSize; }; PostScriptFunction::PostScriptFunction(Object *pFuncObject, Dict *pDict) { m_pCode = NULL; m_nCodeSize = 0; m_bValid = false; if (!Initialize(pDict)) { return; } if (!m_bHasRange) { // TO DO: Error "Type 4 function is missing range" return; } if (!pFuncObject->IsStream()) { // TO DO: Error "Type 4 function isn't a stream" return; } Stream *pStream = pFuncObject->GetStream(); //----- parse the function m_seCodeString = new StringExt(); pStream->Reset(); StringExt *seToken; if (!(seToken = GetToken(pStream)) || seToken->Compare("{")) { // TO DO: Error "Expected '{' at start of PostScript function" MemUtilsFree(seToken); pStream->Close(); return; } if (seToken) delete seToken; int nCodePos = 0; if (!ParseCode(pStream, &nCodePos)) { pStream->Close(); return; } pStream->Close(); m_bValid = true; } PostScriptFunction::PostScriptFunction(PostScriptFunction *pFunc) { memcpy(this, pFunc, sizeof(PostScriptFunction)); m_pCode = (PSObject *)MemUtilsMallocArray(m_nCodeSize, sizeof(PSObject)); memcpy(m_pCode, pFunc->m_pCode, m_nCodeSize * sizeof(PSObject)); m_seCodeString = pFunc->m_seCodeString->Copy(); } PostScriptFunction::~PostScriptFunction() { MemUtilsFree(m_pCode); if (m_seCodeString) delete m_seCodeString; } void PostScriptFunction::Transform(double *pInput, double *pOutput) { PSStack *pStack = new PSStack(); if (!pStack) return; for (int nIndex = 0; nIndex < m_nInputDim; ++nIndex) { pStack->PushReal(pInput[nIndex]); } Exec(pStack, 0); for (int nIndex = m_nOutputDim - 1; nIndex >= 0; --nIndex) { pOutput[nIndex] = pStack->PopNum(); if (pOutput[nIndex] < m_arrRange[nIndex][0]) { pOutput[nIndex] = m_arrRange[nIndex][0]; } else if (pOutput[nIndex] > m_arrRange[nIndex][1]) { pOutput[nIndex] = m_arrRange[nIndex][1]; } } delete pStack; } bool PostScriptFunction::ParseCode(Stream *pStream, int *pnCodePos) { StringExt *seToken; while (1) { if (!(seToken = GetToken(pStream))) { // TO DO: Error "Unexpected end of PostScript function stream" return false; } if (!seToken) return false; char *pCurChar = seToken->GetBuffer(); if (isdigit(*pCurChar) || *pCurChar == '.' || *pCurChar == '-') { bool bReal = false; for (++pCurChar; *pCurChar; ++pCurChar) { if (*pCurChar == '.') { bReal = true; break; } } ResizeCode(*pnCodePos); if (bReal) { m_pCode[*pnCodePos].eType = psReal; m_pCode[*pnCodePos].uReal = atof(seToken->GetBuffer()); } else { m_pCode[*pnCodePos].eType = psInt; m_pCode[*pnCodePos].uInt = atoi(seToken->GetBuffer()); } ++*pnCodePos; delete seToken; } else if (!seToken->Compare("{")) { delete seToken; int nOperatorPos = *pnCodePos; int nElsePos = 0; *pnCodePos += 3; ResizeCode(nOperatorPos + 2); if (!ParseCode(pStream, pnCodePos)) { return false; } if (!(seToken = GetToken(pStream))) { // TO DO: Error "Unexpected end of PostScript function stream" return false; } if (!seToken->Compare("{")) { nElsePos = *pnCodePos; if (!ParseCode(pStream, pnCodePos)) { return false; } delete seToken; if (!(seToken = GetToken(pStream))) { // TO DO: Error "Unexpected end of PostScript function stream" return false; } } else { nElsePos = -1; } if (!seToken->Compare("if")) { if (nElsePos >= 0) { // TO DO: Error "Got 'if' operator with two blocks in PostScript function" return false; } m_pCode[nOperatorPos].eType = psOperator; m_pCode[nOperatorPos].uOperation = psOperationIf; m_pCode[nOperatorPos + 2].eType = psBlock; m_pCode[nOperatorPos + 2].uBlock = *pnCodePos; } else if (!seToken->Compare("ifelse")) { if (nElsePos < 0) { // TO DO: Error "Got 'ifelse' operator with one blocks in PostScript function" return false; } m_pCode[nOperatorPos].eType = psOperator; m_pCode[nOperatorPos].uOperation = psOperationIfelse; m_pCode[nOperatorPos + 1].eType = psBlock; m_pCode[nOperatorPos + 1].uBlock = nElsePos; m_pCode[nOperatorPos + 2].eType = psBlock; m_pCode[nOperatorPos + 2].uBlock = *pnCodePos; } else { // TO DO: Error "Expected if/ifelse operator in PostScript function" delete seToken; return false; } delete seToken; } else if (!seToken->Compare("}")) { delete seToken; ResizeCode(*pnCodePos); m_pCode[*pnCodePos].eType = psOperator; m_pCode[*pnCodePos].uOperation = psOperationReturn; ++*pnCodePos; break; } else { int nFirst = -1; int nSecond = PSOperationsCount; // invariant: psOpNames[a] < tok < psOpNames[b] int nCompareRes = 0; while (nSecond - nFirst > 1) { int nMiddle = (nFirst + nSecond) / 2; nCompareRes = seToken->Compare(c_sPSOperationNames[nMiddle]); if (nCompareRes > 0) { nFirst = nMiddle; } else if (nCompareRes < 0) { nSecond = nMiddle; } else { nFirst = nSecond = nMiddle; } } if (nCompareRes != 0) { // TO DO: Error "Unknown operator in PostScript function" delete seToken; return false; } delete seToken; ResizeCode(*pnCodePos); m_pCode[*pnCodePos].eType = psOperator; m_pCode[*pnCodePos].uOperation = (PSOperations)nFirst; ++*pnCodePos; } } return true; } StringExt *PostScriptFunction::GetToken(Stream *pStream) { int nChar = 0; StringExt *seResult = new StringExt(); bool bComment = false; while (1) { if ((nChar = pStream->GetChar()) == EOF) { break; } m_seCodeString->Append(nChar); if (bComment) { if (nChar == '\x0a' || nChar == '\x0d') { bComment = false; } } else if (nChar == '%') { bComment = true; } else if (!isspace(nChar)) { break; } } if (nChar == '{' || nChar == '}') { seResult->Append((char)nChar); } else if (isdigit(nChar) || nChar == '.' || nChar == '-') { while (1) { seResult->Append((char)nChar); nChar = pStream->LookChar(); if (nChar == EOF || !(isdigit(nChar) || nChar == '.' || nChar == '-')) { break; } pStream->GetChar(); m_seCodeString->Append(nChar); } } else { while (1) { seResult->Append((char)nChar); nChar = pStream->LookChar(); if (nChar == EOF || !isalnum(nChar)) { break; } pStream->GetChar(); m_seCodeString->Append(nChar); } } return seResult; } void PostScriptFunction::ResizeCode(int nNewSize) { if (nNewSize >= m_nCodeSize) { m_nCodeSize += 64; m_pCode = (PSObject *)MemUtilsReallocArray(m_pCode, m_nCodeSize, sizeof(PSObject)); } } void PostScriptFunction::Exec(PSStack *pStack, int codePtr) { int nInteger1 = 0, nInteger2 = 0; double dReal1 = 0, dReal2 = 0; bool bBool1 = false, bBool2 = false; while (1) { switch (m_pCode[codePtr].eType) { case psInt: pStack->PushInt(m_pCode[codePtr++].uInt); break; case psReal: pStack->PushReal(m_pCode[codePtr++].uReal); break; case psOperator: switch (m_pCode[codePtr++].uOperation) { case psOperationAbs: if (pStack->TopIsInt()) { pStack->PushInt(abs(pStack->PopInt())); } else { pStack->PushReal(fabs(pStack->PopNum())); } break; case psOperationAdd: if (pStack->TopTwoAreInts()) { nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); pStack->PushInt(nInteger1 + nInteger2); } else { dReal2 = pStack->PopNum(); dReal1 = pStack->PopNum(); pStack->PushReal(dReal1 + dReal2); } break; case psOperationAnd: if (pStack->TopTwoAreInts()) { nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); pStack->PushInt(nInteger1 & nInteger2); } else { bBool2 = pStack->PopBool(); bBool1 = pStack->PopBool(); pStack->PushBool(bBool1 && bBool2); } break; case psOperationAtan: dReal2 = pStack->PopNum(); dReal1 = pStack->PopNum(); pStack->PushReal(atan2(dReal1, dReal2)); break; case psOperationBitshift: nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); if (nInteger2 > 0) { pStack->PushInt(nInteger1 << nInteger2); } else if (nInteger2 < 0) { pStack->PushInt((int)((unsigned int)nInteger1 >> nInteger2)); } else { pStack->PushInt(nInteger1); } break; case psOperationCeiling: if (!pStack->TopIsInt()) { pStack->PushReal(ceil(pStack->PopNum())); } break; case psOperationCopy: pStack->Copy(pStack->PopInt()); break; case psOperationCos: pStack->PushReal(cos(pStack->PopNum())); break; case psOperationCvi: if (!pStack->TopIsInt()) { pStack->PushInt((int)pStack->PopNum()); } break; case psOperationCvr: if (!pStack->TopIsReal()) { pStack->PushReal(pStack->PopNum()); } break; case psOperationDiv: dReal2 = pStack->PopNum(); dReal1 = pStack->PopNum(); pStack->PushReal(dReal1 / dReal2); break; case psOperationDup: pStack->Copy(1); break; case psOperationEq: if (pStack->TopTwoAreInts()) { nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); pStack->PushBool(nInteger1 == nInteger2); } else if (pStack->TopTwoAreNums()) { dReal2 = pStack->PopNum(); dReal1 = pStack->PopNum(); pStack->PushBool(dReal1 == dReal2); } else { bBool2 = pStack->PopBool(); bBool1 = pStack->PopBool(); pStack->PushBool(bBool1 == bBool2); } break; case psOperationExch: pStack->Roll(2, 1); break; case psOperationExp: dReal2 = pStack->PopNum(); dReal1 = pStack->PopNum(); pStack->PushReal(pow(dReal1, dReal2)); break; case psOperationFalse: pStack->PushBool(false); break; case psOperationFloor: if (!pStack->TopIsInt()) { pStack->PushReal(floor(pStack->PopNum())); } break; case psOperationGe: if (pStack->TopTwoAreInts()) { nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); pStack->PushBool(nInteger1 >= nInteger2); } else { dReal2 = pStack->PopNum(); dReal1 = pStack->PopNum(); pStack->PushBool(dReal1 >= dReal2); } break; case psOperationGt: if (pStack->TopTwoAreInts()) { nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); pStack->PushBool(nInteger1 > nInteger2); } else { dReal2 = pStack->PopNum(); dReal1 = pStack->PopNum(); pStack->PushBool(dReal1 > dReal2); } break; case psOperationIdiv: nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); pStack->PushInt(nInteger1 / nInteger2); break; case psOperationIndex: pStack->Index(pStack->PopInt()); break; case psOperationLe: if (pStack->TopTwoAreInts()) { nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); pStack->PushBool(nInteger1 <= nInteger2); } else { dReal2 = pStack->PopNum(); dReal1 = pStack->PopNum(); pStack->PushBool(dReal1 <= dReal2); } break; case psOperationLn: pStack->PushReal(log(pStack->PopNum())); break; case psOperationLog: pStack->PushReal(log10(pStack->PopNum())); break; case psOperationLt: if (pStack->TopTwoAreInts()) { nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); pStack->PushBool(nInteger1 < nInteger2); } else { dReal2 = pStack->PopNum(); dReal1 = pStack->PopNum(); pStack->PushBool(dReal1 < dReal2); } break; case psOperationMod: nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); pStack->PushInt(nInteger1 % nInteger2); break; case psOperationMul: if (pStack->TopTwoAreInts()) { nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); //~ should check for out-of-range, and push a real instead pStack->PushInt(nInteger1 * nInteger2); } else { dReal2 = pStack->PopNum(); dReal1 = pStack->PopNum(); pStack->PushReal(dReal1 * dReal2); } break; case psOperationNe: if (pStack->TopTwoAreInts()) { nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); pStack->PushBool(nInteger1 != nInteger2); } else if (pStack->TopTwoAreNums()) { dReal2 = pStack->PopNum(); dReal1 = pStack->PopNum(); pStack->PushBool(dReal1 != dReal2); } else { bBool2 = pStack->PopBool(); bBool1 = pStack->PopBool(); pStack->PushBool(bBool1 != bBool2); } break; case psOperationNeg: if (pStack->TopIsInt()) { pStack->PushInt(-pStack->PopInt()); } else { pStack->PushReal(-pStack->PopNum()); } break; case psOperationNot: if (pStack->TopIsInt()) { pStack->PushInt(~pStack->PopInt()); } else { pStack->PushBool(!pStack->PopBool()); } break; case psOperationOr: if (pStack->TopTwoAreInts()) { nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); pStack->PushInt(nInteger1 | nInteger2); } else { bBool2 = pStack->PopBool(); bBool1 = pStack->PopBool(); pStack->PushBool(bBool1 || bBool2); } break; case psOperationPop: pStack->Pop(); break; case psOperationRoll: nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); pStack->Roll(nInteger1, nInteger2); break; case psOperationRound: if (!pStack->TopIsInt()) { dReal1 = pStack->PopNum(); pStack->PushReal((dReal1 >= 0) ? floor(dReal1 + 0.5) : ceil(dReal1 - 0.5)); } break; case psOperationSin: pStack->PushReal(sin(pStack->PopNum())); break; case psOperationSqrt: pStack->PushReal(sqrt(pStack->PopNum())); break; case psOperationSub: if (pStack->TopTwoAreInts()) { nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); pStack->PushInt(nInteger1 - nInteger2); } else { dReal2 = pStack->PopNum(); dReal1 = pStack->PopNum(); pStack->PushReal(dReal1 - dReal2); } break; case psOperationTrue: pStack->PushBool(true); break; case psOperationTruncate: if (!pStack->TopIsInt()) { dReal1 = pStack->PopNum(); pStack->PushReal((dReal1 >= 0) ? floor(dReal1) : ceil(dReal1)); } break; case psOperationXor: if (pStack->TopTwoAreInts()) { nInteger2 = pStack->PopInt(); nInteger1 = pStack->PopInt(); pStack->PushInt(nInteger1 ^ nInteger2); } else { bBool2 = pStack->PopBool(); bBool1 = pStack->PopBool(); pStack->PushBool(bBool1 ^ bBool2); } break; case psOperationIf: bBool1 = pStack->PopBool(); if (bBool1) { Exec(pStack, codePtr + 2); } codePtr = m_pCode[codePtr + 1].uBlock; break; case psOperationIfelse: bBool1 = pStack->PopBool(); if (bBool1) { Exec(pStack, codePtr + 2); } else { Exec(pStack, m_pCode[codePtr].uBlock); } codePtr = m_pCode[codePtr + 1].uBlock; break; case psOperationReturn: return; } break; default: // TO DO: Error "Internal: bad object in PostScript function code" break; } } } }