// File: Parse.cpp // Author: Michael Marr (mikemarr) // // History: // -@- 09/23/97 (mikemarr) copied from projects\vector2d #include "StdAfx.h" #include "Parse.h" #define fGSCALE 1.f class CAdobeFormatConverter { public: CAdobeFormatConverter(); ~CAdobeFormatConverter() {} HRESULT Parse(const char *pData, DWORD nFileLength, RenderCmd **ppCmds); private: HRESULT ParseProlog(); HRESULT ParseScript(); HRESULT ParseSetup(); HRESULT ParseObjects(); HRESULT ParseCompoundPath(); HRESULT ParsePath(); HRESULT ParsePaintStyle(const char *&pEnd); HRESULT ParsePathGeometry(const char *pEnd); HRESULT ParseTrailers(); private: void EatLine(); const char * FindNextLine(const char *pch); const char * FindLF(const char *pch); const char * FindSpace(const char *pch); private: const char * m_pData, *m_pLimit; float m_fWidth, m_fHeight; // float m_fMaxHeight; bool m_bNoBrush, m_bNoPen; DXFPOINT m_rgPoints[nMAXPOINTS]; DXFPOINT * m_pCurPoint; BYTE m_rgCodes[nMAXPOINTS]; BYTE * m_pCurCode; RenderCmd m_rgRenderCmds[nMAXPOLYS]; RenderCmd * m_pCurRenderCmd; PolyInfo m_rgPolyInfos[nMAXPOLYS]; PolyInfo * m_pCurPolyInfo; BrushInfo m_rgBrushInfos[nMAXBRUSHES]; BrushInfo * m_pCurBrushInfo; PenInfo m_rgPenInfos[nMAXPENS]; PenInfo * m_pCurPenInfo; }; inline bool mmIsSpace(char ch) { return ((ch == ' ') || (ch == chLINEFEED) || (ch == chCARRIAGERETURN)); // return isspace(ch) != 0; } inline bool mmIsDigit(char ch) { return ((ch >= '0') && (ch <= '9')); // return isdigit(ch) != 0; } float mmSimpleAtoF(const char *&pData) { const char *pSrc = pData; // eat white space while (mmIsSpace(*pSrc)) pSrc++; bool bNeg; if (*pSrc == '-') { bNeg = true; pSrc++; } else { bNeg = false; } // get digits before the decimal point float f; if (mmIsDigit(*pSrc)) { f = float(*pSrc++ - '0'); while (mmIsDigit(*pSrc)) f = f * 10.f + float(*pSrc++ - '0'); } else { f = 0.f; } if (*pSrc == '.') pSrc++; // get digits after the decimal point float fDec = 0.1f; while (mmIsDigit(*pSrc)) { f += (float(*pSrc++ - '0') * fDec); fDec *= 0.1f; } // REVIEW: assume no exponent for now pData = pSrc; return (bNeg ? -f : f); } inline const char * CAdobeFormatConverter::FindLF(const char *pch) { // find the linefeed character while ((*pch != chLINEFEED) && (*pch != chCARRIAGERETURN)) pch++; MMASSERT(pch <= m_pLimit); return pch; } inline const char * CAdobeFormatConverter::FindNextLine(const char *pch) { // find the linefeed character while (*pch++ != chLINEFEED); // check if there is also carriage return if (*pch == chCARRIAGERETURN) pch++; MMASSERT(pch <= m_pLimit); return pch; } inline const char * CAdobeFormatConverter::FindSpace(const char *pch) { // find the linefeed character while (!mmIsSpace(*pch)) pch++; MMASSERT(pch <= m_pLimit); return pch; } inline void CAdobeFormatConverter::EatLine() { m_pData = FindNextLine(m_pData); } CAdobeFormatConverter::CAdobeFormatConverter() { m_pData = m_pLimit = NULL; m_fWidth = m_fHeight = 0.f; // m_fMaxHeight = 0.f; m_bNoBrush = m_bNoPen = true; } HRESULT CAdobeFormatConverter::Parse(const char *pData, DWORD nFileLength, RenderCmd **ppCmds) { // MMTRACE("Parse\n"); HRESULT hr = S_OK; if (ppCmds == NULL) return E_POINTER; if (!pData || !nFileLength) return E_INVALIDARG; m_pData = pData; m_pLimit = pData + nFileLength; // intialize command storage stuff m_pCurPoint = m_rgPoints; m_pCurCode = m_rgCodes; m_pCurPolyInfo = m_rgPolyInfos; m_pCurRenderCmd = m_rgRenderCmds; m_pCurBrushInfo = m_rgBrushInfos; m_pCurPenInfo = m_rgPenInfos; CHECK_HR(hr = ParseProlog()); CHECK_HR(hr = ParseScript()); e_Exit: // write a stop command to the end m_pCurRenderCmd->nType = typeSTOP; m_pCurRenderCmd->pvData = NULL; *ppCmds = m_rgRenderCmds; return hr; } HRESULT CAdobeFormatConverter::ParseProlog() { // MMTRACE("ParseProlog\n"); const char *szSearch; // extract the image dimensions float f1, f2; // bounding box is supposed to be a required field with the proper numbers szSearch = "%%BoundingBox:"; m_pData = strstr(m_pData, szSearch); m_pData = FindSpace(m_pData); f1 = mmSimpleAtoF(m_pData); f2 = mmSimpleAtoF(m_pData); m_fWidth = mmSimpleAtoF(m_pData); m_fHeight = mmSimpleAtoF(m_pData); // if (sscanf(m_pData, "%f %f %f %f", &f1, &f2, &m_fWidth, &m_fHeight) != 4) // return E_FAIL; if ((m_fWidth <= 0.f) || (m_fHeight < 0.f)) return E_FAIL; // m_fMaxHeight = float(m_nHeight); // search until we find end string szSearch = "%%EndProlog"; m_pData = strstr(m_pData, szSearch); if (m_pData == NULL) return E_FAIL; EatLine(); return S_OK; } HRESULT CAdobeFormatConverter::ParseScript() { // MMTRACE("ParseScript\n"); HRESULT hr; if (FAILED(hr = ParseSetup()) || FAILED(hr = ParseObjects()) || FAILED(hr = ParseTrailers())) return hr; return S_OK; } HRESULT CAdobeFormatConverter::ParseSetup() { // MMTRACE("ParseSetup\n"); const char *szSearch; // search until we find end string szSearch = "%%EndSetup"; m_pData = strstr(m_pData, szSearch); if (m_pData == NULL) return E_FAIL; EatLine(); return S_OK; } HRESULT CAdobeFormatConverter::ParseObjects() { // MMTRACE("ParseObjects\n"); HRESULT hr = S_OK; const char *szPageTrailer = "%%PageTrailer"; const char *szTrailer = "%%Trailer"; int cPageTrailer = strlen(szPageTrailer); int cTrailer = strlen(szTrailer); // process dimensions /* const char *pEnd; pEnd = FindLF(m_pData); // pEnd = strchr(m_pData, '\n'); if ((pEnd[-1] == 'b') && (pEnd[-2] == 'L')) { // get the dimensions out int n1, n2, n3, n4, n5, n6, n7, n8; if ((sscanf(m_pData, "%d %d %d %d %d %d %d %d %d %d %d", &n1, &n2, &n3, &n4, &n5, &n6, &n7, &n8, &m_nWidth, &m_nHeight) != 10) || (m_nWidth <= 0) || (m_nHeight < 0)) { return E_FAIL; } m_fMaxHeight = float(m_nHeight); m_pData = FindNextLine(pEnd); } pEnd = FindLF(m_pData); // pEnd = strchr(m_pData, '\n'); if ((pEnd[-1] == 'n') && (pEnd[-2] == 'L')) { // skip layer information m_pData = FindNextLine(pEnd); } */ for (;;) { switch (m_pData[0]) { case '%': if ((strncmp(m_pData, szPageTrailer, cPageTrailer) == 0) || (strncmp(m_pData, szTrailer, cTrailer) == 0)) { // end of object definitions goto e_Exit; } else { // comment EatLine(); } break; case '*': if (m_pData[1] == 'u') CHECK_HR(hr = ParseCompoundPath()); else { hr = E_FAIL; goto e_Exit; } break; default: CHECK_HR(hr = ParsePath()); break; } } e_Exit: if (hr == S_OK) EatLine(); return hr; } HRESULT CAdobeFormatConverter::ParseCompoundPath() { // MMTRACE("ParseCompoundPath\n"); HRESULT hr = S_OK; // remove the "*u" MMASSERT((m_pData[0] == '*') && (m_pData[1] == 'u')); // if (strncmp(m_pData, "*u", 2) != 0) // return E_UNEXPECTED; EatLine(); while (m_pData[0] != '*') CHECK_HR(hr = ParsePath()); // remove the "*U" MMASSERT((m_pData[0] == '*') && (m_pData[1] == 'U')); // if (strncmp(m_pData, "*U", 2) != 0) // return E_UNEXPECTED; EatLine(); e_Exit: return hr; } inline UINT GetUInt(const char *pData) { return (UINT) atoi(pData); } typedef DWORD FP; #define nEXPBIAS 127 #define nEXPSHIFTS 23 #define nEXPLSB (1 << nEXPSHIFTS) #define maskMANT (nEXPLSB - 1) #define FloatToFixed08(nDst, fSrc) MACSTART \ float fTmp = fSrc; \ DWORD nRaw = *((FP *) &(fTmp)); \ if (nRaw < ((nEXPBIAS + 23 - 31) << nEXPSHIFTS)) \ nDst = 0; \ else \ nDst = ((nRaw | nEXPLSB) << 8) >> ((nEXPBIAS + 23) - (nRaw >> nEXPSHIFTS)); \ MACEND HRESULT CAdobeFormatConverter::ParsePaintStyle(const char *&pEnd) { HRESULT hr = S_OK; BOOL bNotDone = TRUE; // int nLineJoin = 1, nLineCap = 1; float fLineWidth = 1.f; float fGrayFill, fGrayStroke; float fCyan, fYellow, fMagenta, fBlack; bool bColorFill = false, bGrayFill = false, bGrayStroke = false; // parse paint style for (; pEnd; pEnd = FindLF(m_pData)) { switch(pEnd[-1]) { // // path attributes // case 'd': // process dash // REVIEW: skip this for now -- assume NULL pattern break; case 'j': // process line join type // REVIEW: skip this for now, since it is always 1 // nLineJoin = mmSimpleAtoI(m_pData); break; case 'J': // process line cap type // REVIEW: skip this for now, since it is always 1 // nLineCap = mmSimpleAtoI(m_pData); break; case 'w': // process line width // REVIEW: skip this for now, since it is always 1.f // fLineWidth = mmSimpleAtoF(m_pData); break; // // fill color // case 'g': // process gray color for fill fGrayFill = mmSimpleAtoF(m_pData); bGrayFill = true; break; case 'k': // process color fCyan = mmSimpleAtoF(m_pData); fMagenta = mmSimpleAtoF(m_pData); fYellow = mmSimpleAtoF(m_pData); fBlack = mmSimpleAtoF(m_pData); bColorFill = true; break; // // stroke color // case 'G': // process gray color for stroke fGrayStroke = mmSimpleAtoF(m_pData); bGrayStroke = true; break; default: goto Exit; break; } m_pData = FindNextLine(pEnd); // m_pData = pEnd + 1; } Exit: // output GDI commands // // create a brush // if (bColorFill || bGrayFill) { static DWORD nLastRed = 256, nLastGreen = 256, nLastBlue = 256; DWORD nTmpRed, nTmpGreen, nTmpBlue; if (bColorFill) { FloatToFixed08(nTmpRed, fCyan + fBlack); CLAMPMAX(nTmpRed, 255); nTmpRed = 255 - nTmpRed; FloatToFixed08(nTmpGreen, fMagenta + fBlack); CLAMPMAX(nTmpGreen, 255); nTmpGreen = 255 - nTmpGreen; FloatToFixed08(nTmpBlue, fYellow + fBlack); CLAMPMAX(nTmpBlue, 255); nTmpBlue = 255 - nTmpBlue; } else if (bGrayFill) { DWORD nTmpGray; FloatToFixed08(nTmpGray, fGrayFill); CLAMPMAX(nTmpGray, 255); nTmpRed = nTmpGreen = nTmpBlue = nTmpGray; } if ((nLastRed != nTmpRed) || (nLastGreen != nTmpGreen) || (nLastBlue != nTmpBlue)) { // define a new brush nLastRed = nTmpRed; nLastGreen = nTmpGreen; nLastBlue = nTmpBlue; // fprintf(m_pFile, "\t// select a new brush\n"); // fprintf(m_pFile, "\tBrush.Color = DXSAMPLE(255, %d, %d, %d);\n", nRed, nGreen, nBlue); // fprintf(m_pFile, "\tpDX2D->SetBrush(&Brush);\n\n"); m_pCurBrushInfo->Color = DXSAMPLE(255, BYTE(nTmpRed), BYTE(nTmpGreen), BYTE(nTmpBlue)); m_pCurRenderCmd->nType = typeBRUSH; m_pCurRenderCmd->pvData = (void *) m_pCurBrushInfo++; m_pCurRenderCmd++; m_bNoBrush = false; } } // create a pen if (bGrayStroke) { static bool bPenInit = false; // we only have one pen in the simpsons.ai if (!bPenInit) { // if ((fGrayStroke != 0.f) || (nLineJoin != 1) || (nLineCap != 1)) { if (fGrayStroke != 0.f) { MMTRACE("error: can not support pen type\n"); return E_FAIL; } bPenInit = true; // fprintf(m_pFile, "\t// select a new pen\n"); // fprintf(m_pFile, "\tPen.Color = DXSAMPLE(255, 0, 0, 0);\n"); // fprintf(m_pFile, "\tPen.Width = %.2ff;\n", fLineWidth * fGSCALE); // fprintf(m_pFile, "\tPen.Style = PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_ROUND | PS_JOIN_ROUND;\n"); // fprintf(m_pFile, "\tpDX2D->SetPen(&Pen);\n\n"); // REVIEW: only can make one kind of pen right now m_pCurPenInfo->Color = DXSAMPLE(255, 0, 0, 0); m_pCurPenInfo->fWidth = fLineWidth * fGSCALE; m_pCurPenInfo->dwStyle = PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_ROUND | PS_JOIN_ROUND; m_pCurRenderCmd->nType = typePEN; m_pCurRenderCmd->pvData = (void *) m_pCurPenInfo++; m_pCurRenderCmd++; m_bNoPen = false; } } return S_OK; } #define GetCoordX(_fX) ((_fX) * fGSCALE) #define GetCoordY(_fY) ((m_fHeight - (_fY)) * fGSCALE) HRESULT CAdobeFormatConverter::ParsePathGeometry(const char *pEnd) { HRESULT hr = S_OK; // float fX1, fY1, fXBez1, fYBez1, fXBez2, fYBez2; m_pCurPolyInfo->pPoints = m_pCurPoint; m_pCurPolyInfo->pCodes = m_pCurCode; // parse path geometry DWORD cPoints = 0; bool bFlatten = false; for (; pEnd; pEnd = FindLF(m_pData)) { switch(pEnd[-1]) { case 'm': // fprintf(m_pFile, "\t// define geometry path\n"); // sscanf(m_pData, "%f %f", &fX1, &fY1); // fprintf(m_pFile, "\tppt = rgpt; pb = rgCodes;\n"); // fprintf(m_pFile, "\tppt->x = %.2ff; ppt->y = %.2ff; *pb++ = PT_MOVETO; ppt++;\n", GetCoordX(fX1), GetCoordY(fY1)); m_pCurPoint->x = GetCoordX(mmSimpleAtoF(m_pData)); m_pCurPoint->y = GetCoordY(mmSimpleAtoF(m_pData)); m_pCurPoint++; *m_pCurCode++ = PT_MOVETO; cPoints++; break; case 'L': case 'l': // sscanf(m_pData, "%f %f", &fX1, &fY1); // fprintf(m_pFile, "\tppt->x = %.2ff; ppt->y = %.2ff; *pb++ = PT_LINETO; ppt++;\n", GetCoordX(fX1), GetCoordY(fY1)); m_pCurPoint->x = GetCoordX(mmSimpleAtoF(m_pData)); m_pCurPoint->y = GetCoordY(mmSimpleAtoF(m_pData)); m_pCurPoint++; *m_pCurCode++ = PT_LINETO; cPoints++; break; case 'C': case 'c': bFlatten = true; m_pCurPoint[0].x = GetCoordX(mmSimpleAtoF(m_pData)); m_pCurPoint[0].y = GetCoordY(mmSimpleAtoF(m_pData)); m_pCurPoint[1].x = GetCoordX(mmSimpleAtoF(m_pData)); m_pCurPoint[1].y = GetCoordY(mmSimpleAtoF(m_pData)); m_pCurPoint[2].x = GetCoordX(mmSimpleAtoF(m_pData)); m_pCurPoint[2].y = GetCoordY(mmSimpleAtoF(m_pData)); m_pCurPoint += 3; m_pCurCode[0] = PT_BEZIERTO; m_pCurCode[1] = PT_BEZIERTO; m_pCurCode[2] = PT_BEZIERTO; m_pCurCode += 3; cPoints += 3; // sscanf(m_pData, "%f %f %f %f %f %f", &fXBez1, &fYBez1, &fXBez2, &fYBez2, &fX1, &fY1); // fprintf(m_pFile, "\tppt[0].x = %.2ff; ppt[0].y = %.2ff; pb[0] = PT_BEZIERTO;\n", GetCoordX(fXBez1), GetCoordY(fYBez1)); // fprintf(m_pFile, "\tppt[1].x = %.2ff; ppt[1].y = %.2ff; pb[1] = PT_BEZIERTO;\n", GetCoordX(fXBez2), GetCoordY(fYBez2)); // fprintf(m_pFile, "\tppt[2].x = %.2ff; ppt[2].y = %.2ff; pb[2] = PT_BEZIERTO; ppt += 3; pb += 3;\n", GetCoordX(fX1), GetCoordY(fY1)); break; default: goto Exit; break; } // skip the line m_pData = FindNextLine(pEnd); } Exit: // create the path // char *pFillType = (bFlatten ? "0" : "DX2D_NO_FLATTEN"); if (cPoints) { DWORD dwFlags; switch(pEnd[-1]) { case 'f': // close path and fill if (m_bNoBrush) { // fprintf(m_pFile, "\tpDX2D->SetBrush(&Brush);\n"); m_nLines++; m_bNoBrush = false; } if (m_bNoPen == false) { // fprintf(m_pFile, "\tpDX2D->SetPen(NULL);\n"); m_nLines++; m_bNoPen = true; } dwFlags = DX2D_FILL; break; case 'S': // stroke path if (m_bNoPen) { // fprintf(m_pFile, "\tpDX2D->SetPen(&Pen);\n"); m_nLines++; m_bNoPen = false; } if (m_bNoBrush == false) { // fprintf(m_pFile, "\tpDX2D->SetBrush(NULL);\n"); m_nLines++; m_bNoBrush = true; } dwFlags = DX2D_STROKE; break; default: MMTRACE("error: unknown render mode -- aborting\n"); return E_FAIL; break; } // fprintf(m_pFile, "\tpDX2D->AAPolyDraw(rgpt, rgCodes, %d, %s);\n", iPoint, pFillType); m_pCurPolyInfo->cPoints = cPoints; m_pCurPolyInfo->dwFlags = dwFlags | (bFlatten ? 0 : DX2D_NO_FLATTEN); m_pCurRenderCmd->nType = typePOLY; m_pCurRenderCmd->pvData = (PolyInfo *) m_pCurPolyInfo++; m_pCurRenderCmd++; m_pData = FindNextLine(pEnd); } return S_OK; } HRESULT CAdobeFormatConverter::ParsePath() { // MMTRACE("ParsePath\n"); HRESULT hr; const char *pStart = m_pData, *pEnd = FindLF(m_pData); if (FAILED(hr = ParsePaintStyle(pEnd))) return hr; if (FAILED(hr = ParsePathGeometry(pEnd))) return hr; // skip it if we don't know how to deal with it if (pStart == m_pData) { // if ((m_pData[0] != 'L') || (m_pData[1] != 'B')) { // MMTRACE("warning: control data of unknown type -- ignoring line\n"); // } m_pData = FindNextLine(pEnd); } return hr; } HRESULT CAdobeFormatConverter::ParseTrailers() { return S_OK; } HRESULT OpenFileMapping(const char *szFilename, LPHANDLE phMapping, DWORD *pnFileLength) { MMASSERT(szFilename && phMapping && pnFileLength); HRESULT hr = S_OK; HANDLE hFile = NULL, hMapping = NULL; DWORD nFileLength = 0, dwHighSize = 0; MMTRACE("Opening File: %s\n", szFilename); // open the file hFile = CreateFile(szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0); if ((hFile == NULL) || (hFile == INVALID_HANDLE_VALUE)) { MMTRACE("error: file not found - %s\n", szFilename); return STG_E_FILENOTFOUND; } // get the length of the file if (((nFileLength = GetFileSize(hFile, &dwHighSize)) == 0xFFFFFFFF) || dwHighSize || // create a file mapping object ((hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) == NULL)) { MMTRACE("error: creating file mapping\n"); hr = E_FAIL; } MMTRACE("\tLength: %d\n", nFileLength); if (hFile) CloseHandle(hFile); *phMapping = hMapping; *pnFileLength = nFileLength; return hr; } #define szDEFFILENAME "\\dtrans\\tools\\simpsons\\simpsons.ai" HRESULT ParseAIFile(const char *szFilename, RenderCmd **ppCmds) { HRESULT hr = S_OK; static CAdobeFormatConverter afc; static RenderCmd s_CmdStop = {typeSTOP, NULL}; DWORD nStartTick, nEndTick; DWORD nFileLength; HANDLE hMapping = NULL; char *pData = NULL; if (szFilename == NULL) szFilename = szDEFFILENAME; nStartTick = GetTickCount(); CHECK_HR(hr = OpenFileMapping(szFilename, &hMapping, &nFileLength)); // create a map view if ((pData = (char *) MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0)) == NULL) { hr = E_FAIL; goto e_Exit; } CHECK_HR(hr = afc.Parse(pData, nFileLength, ppCmds)); e_Exit: if (pData) UnmapViewOfFile(pData); if (hMapping) CloseHandle(hMapping); if (FAILED(hr)) { // set to the null command list *ppCmds = &s_CmdStop; MMTRACE("\terror parsing file\n"); } else { nEndTick = GetTickCount(); sprintf(g_rgchTmpBuf, "\tParse Time: %d\n", nEndTick - nStartTick); OutputDebugString(g_rgchTmpBuf); } return hr; }