/** ******************************************************************************* * @file parserSVG/Base.cpp * @brief basic Element parsing (Sources) * @author Edouard DUPIN * @date 20/03/2012 * @par Project * parserSVG * * @par Copyright * Copyright 2011 Edouard DUPIN, all right reserved * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY. * * Licence summary : * You can modify and redistribute the sources code and binaries. * You can send me the bug-fix * * Term of the licence in in the file licence.txt. * ******************************************************************************* */ #include #include #include svg::Base::Base(PaintState parentPaintState) { // copy the parent painting properties ... m_paint = parentPaintState; } /** * @brief Parse the transform balise C String. * @param[in] inputString String data inside the transform attribute * @param[in,out] baseMatrice matrice that must be update * @return --- */ void svg::Base::ParseTransform(TiXmlNode *node) { SVG_CHECK_INOUT(node); const char * inputString = (char*)node->ToElement()->Attribute("transform"); if (NULL == inputString) { return; } SVG_VERBOSE("find transform : \"" << inputString << "\""); char tmpData[2048]; for (int32_t iii=0; inputString[iii]!='\0' && iii<2047; iii++) { if (inputString[iii] == ',') { tmpData[iii] = ' '; } else { tmpData[iii] = inputString[iii]; } // end of the string ... tmpData[iii+1] = '\0'; } SVG_VERBOSE("find transform : \"" << tmpData << "\""); double matrix[6]; float angle, xxx, yyy; int32_t n; char * pointerOnData = tmpData; while (*pointerOnData) { if (sscanf(pointerOnData, "matrix (%lf %lf %lf %lf %lf %lf) %n", &matrix[0], &matrix[1], &matrix[2], &matrix[3], &matrix[4], &matrix[5], &n) == 6) { m_transformMatrix.load_from(matrix); } else if (sscanf(pointerOnData, "translate (%f %f) %n", &xxx, &yyy, &n) == 2) { m_transformMatrix *= agg::trans_affine_translation(xxx, yyy); SVG_VERBOSE("Translate : " << xxx << ", " << yyy); } else if (sscanf(pointerOnData, "translate (%f) %n", &xxx, &n) == 1) { m_transformMatrix *= agg::trans_affine_translation(xxx, 0); SVG_VERBOSE("Translate : " << xxx << ", " << 0); } else if (sscanf(pointerOnData, "scale (%f %f) %n", &xxx, &yyy, &n) == 2) { m_transformMatrix *= agg::trans_affine_scaling(xxx, yyy); SVG_VERBOSE("Scale : " << xxx << ", " << yyy); } else if (sscanf(pointerOnData, "scale (%f) %n", &xxx, &n) == 1) { m_transformMatrix *= agg::trans_affine_scaling(xxx, xxx); SVG_VERBOSE("Scale : " << xxx << ", " << xxx); } else if (sscanf(pointerOnData, "rotate (%f %f %f) %n", &angle, &xxx, &yyy, &n) == 3) { angle = angle / 180 * M_PI; m_transformMatrix *= agg::trans_affine_translation(-xxx, -yyy); m_transformMatrix *= agg::trans_affine_rotation(angle); m_transformMatrix *= agg::trans_affine_translation(xxx, yyy); } else if (sscanf(pointerOnData, "rotate (%f) %n", &angle, &n) == 1) { angle = angle / 180 * M_PI; SVG_VERBOSE("rotate : " << angle << "rad, " << (angle/M_PI*180) << "°"); m_transformMatrix *= agg::trans_affine_rotation(angle); } else if (sscanf(pointerOnData, "skewX (%f) %n", &angle, &n) == 1) { angle = angle / 180 * M_PI; SVG_VERBOSE("skewX : " << angle << "rad, " << (angle/M_PI*180) << "\Uffffffff"); m_transformMatrix *= agg::trans_affine_skewing(angle, 0.0); } else if (sscanf(pointerOnData, "skewY (%f) %n", &angle, &n) == 1) { angle = angle / 180 * M_PI; SVG_VERBOSE("skewY : " << angle << "rad, " << (angle/M_PI*180) << "\Uffffffff"); m_transformMatrix *= agg::trans_affine_skewing(0.0, angle); } else { break; } pointerOnData += n; } } /** * @brief Parse x, y, width, height attribute of the xml node * @param[in] node XML node * @param[out] pos parsed position * @param[out] size parsed dimention * @return --- */ void svg::Base::ParsePosition(const TiXmlNode *node, coord2D_ts &pos, coord2D_ts &size) { pos.x = 0; pos.y = 0; size.x = m_paint.viewPort.x; size.y = m_paint.viewPort.y; const char * content = node->ToElement()->Attribute("x"); if (NULL != content) { pos.x = ParseLength(content); } content = node->ToElement()->Attribute("y"); if (NULL != content) { pos.y = ParseLength(content); } content = node->ToElement()->Attribute("width"); if (NULL != content) { size.x = ParseLength(content); } content = node->ToElement()->Attribute("height"); if (NULL != content) { size.y = ParseLength(content); } } /** * @brief Parse a lenght of the xml element * @param[in] dataInput Data C String with the printed lenght * @return standart number of pixels */ etkFloat_t svg::Base::ParseLength(const char *dataInput) { int32_t numLength = strspn(dataInput, "0123456789+-."); const char *unit = dataInput + numLength; //SVG_INFO(" ==> \"" << dataInput << "\""); etkFloat_t n = atof(dataInput); //SVG_INFO(" ==> ?? = " << n ); etkFloat_t font_size = 20.0; // note : ";" is for the parsing of the style elements ... if (unit[0] == '\0' || unit[0] == ';' ) { return n; } else if (unit[0] == '%') { // xxx % return n / 100.0 * m_paint.viewPort.x; } else if (unit[0] == 'e' && unit[1] == 'm') { // xxx em return n * font_size; } else if (unit[0] == 'e' && unit[1] == 'x') { // xxx ex return n / 2.0 * font_size; } else if (unit[0] == 'p' && unit[1] == 'x') { // xxx px return n; } else if (unit[0] == 'p' && unit[1] == 't') { // xxx pt return n * 1.25; } else if (unit[0] == 'p' && unit[1] == 'c') { // xxx pc return n * 15.0; } else if (unit[0] == 'm' && unit[1] == 'm') { // xxx mm return n * 3.543307; } else if (unit[0] == 'c' && unit[1] == 'm') { // xxx cm return n * 35.43307; } else if (unit[0] == 'i' && unit[1] == 'n') { // xxx in return n * 90; } return 0; } // return the next char position ... (after ';' or NULL) const char * extractPartOfStyle(const char * input, char * outputType, char * outputData, int32_t maxLen) { if (*input == '\0') { return NULL; } int32_t jjj = 0; const char * outputPointer = NULL; outputType[maxLen-1] = '\0'; outputType[0] = '\0'; outputData[maxLen-1] = '\0'; outputData[0] = '\0'; char * output = outputType; for( int32_t iii=0; iiiToElement()->Attribute("fill"); if (NULL != content) { m_paint.fill = ParseColor(content); } content = node->ToElement()->Attribute("stroke"); if (NULL != content) { m_paint.stroke = ParseColor(content); } content = node->ToElement()->Attribute("stroke-width"); if (NULL != content) { m_paint.strokeWidth = ParseLength(content); } content = node->ToElement()->Attribute("style"); if (NULL != content) { char outputType[1024] = ""; char outputValue[1024] = ""; for( const char *sss=extractPartOfStyle(content, outputType, outputValue, 1024); NULL != sss; sss=extractPartOfStyle(sss, outputType, outputValue, 1024) ) { //SVG_INFO(" style parse : \"" << outputType << "\" with value : \"" << outputValue << "\""); if (0 == strcmp(outputType, "fill") ) { m_paint.fill = ParseColor(outputValue); } else if (0 == strcmp(outputType, "stroke") ) { m_paint.stroke = ParseColor(outputValue); } else if (0 == strcmp(outputType, "stroke-width") ) { m_paint.strokeWidth = ParseLength(outputValue); } else if (0 == strcmp(outputType, "opacity") ) { etkFloat_t opacity = ParseLength(outputValue); opacity = etk_max(0.0, etk_min(1.0, opacity)); m_paint.fill.alpha = opacity*0xFF; m_paint.stroke.alpha = opacity*0xFF; } else if (0 == strcmp(outputType, "fill-opacity") ) { etkFloat_t opacity = ParseLength(outputValue); opacity = etk_max(0.0, etk_min(1.0, opacity)); m_paint.fill.alpha = opacity*0xFF; } else if (0 == strcmp(outputType, "stroke-opacity") ) { etkFloat_t opacity = ParseLength(outputValue); opacity = etk_max(0.0, etk_min(1.0, opacity)); m_paint.stroke.alpha = opacity*0xFF; } else if (0 == strcmp(outputType, "fill-rule") ) { if (0 == strcmp(outputValue, "nonzero") ) { m_paint.flagEvenOdd = false; } else if (0 == strcmp(outputValue, "evenodd") ) { m_paint.flagEvenOdd = true; } else { SVG_ERROR("not know " << outputType << " value : \"" << outputValue << "\", not in [nonzero,evenodd]"); } } else { SVG_ERROR("not know painting element in style balise : \"" << outputType << "\" with value : \"" << outputValue << "\""); } } } } typedef struct { const char * colorName; uint32_t color; } colorList_ts; const colorList_ts listOfColor[] = { { "none", 0x00000000}, { "AliceBlue", 0xF0F8FFFF}, { "AntiqueWhite", 0xFAEBD7FF}, { "Aqua", 0x00FFFFFF}, { "Aquamarine", 0x7FFFD4FF}, { "Azure", 0xF0FFFFFF}, { "Beige", 0xF5F5DCFF}, { "Bisque", 0xFFE4C4FF}, { "Black", 0x000000FF}, { "BlanchedAlmond", 0xFFEBCDFF}, { "Blue", 0x0000FFFF}, { "BlueViolet", 0x8A2BE2FF}, { "Brown", 0xA52A2AFF}, { "BurlyWood", 0xDEB887FF}, { "CadetBlue", 0x5F9EA0FF}, { "Chartreuse", 0x7FFF00FF}, { "Chocolate", 0xD2691EFF}, { "Coral", 0xFF7F50FF}, { "CornflowerBlue", 0x6495EDFF}, { "Cornsilk", 0xFFF8DCFF}, { "Crimson", 0xDC143CFF}, { "Cyan", 0x00FFFFFF}, { "DarkBlue", 0x00008BFF}, { "DarkCyan", 0x008B8BFF}, { "DarkGoldenRod", 0xB8860BFF}, { "DarkGray", 0xA9A9A9FF}, { "DarkGrey", 0xA9A9A9FF}, { "DarkGreen", 0x006400FF}, { "DarkKhaki", 0xBDB76BFF}, { "DarkMagenta", 0x8B008BFF}, { "DarkOliveGreen", 0x556B2FFF}, { "Darkorange", 0xFF8C00FF}, { "DarkOrchid", 0x9932CCFF}, { "DarkRed", 0x8B0000FF}, { "DarkSalmon", 0xE9967AFF}, { "DarkSeaGreen", 0x8FBC8FFF}, { "DarkSlateBlue", 0x483D8BFF}, { "DarkSlateGray", 0x2F4F4FFF}, { "DarkSlateGrey", 0x2F4F4FFF}, { "DarkTurquoise", 0x00CED1FF}, { "DarkViolet", 0x9400D3FF}, { "DeepPink", 0xFF1493FF}, { "DeepSkyBlue", 0x00BFFFFF}, { "DimGray", 0x696969FF}, { "DimGrey", 0x696969FF}, { "DodgerBlue", 0x1E90FFFF}, { "FireBrick", 0xB22222FF}, { "FloralWhite", 0xFFFAF0FF}, { "ForestGreen", 0x228B22FF}, { "Fuchsia", 0xFF00FFFF}, { "Gainsboro", 0xDCDCDCFF}, { "GhostWhite", 0xF8F8FFFF}, { "Gold", 0xFFD700FF}, { "GoldenRod", 0xDAA520FF}, { "Gray", 0x808080FF}, { "Grey", 0x808080FF}, { "Green", 0x008000FF}, { "GreenYellow", 0xADFF2FFF}, { "HoneyDew", 0xF0FFF0FF}, { "HotPink", 0xFF69B4FF}, { "IndianRed ", 0xCD5C5CFF}, { "Indigo ", 0x4B0082FF}, { "Ivory", 0xFFFFF0FF}, { "Khaki", 0xF0E68CFF}, { "Lavender", 0xE6E6FAFF}, { "LavenderBlush", 0xFFF0F5FF}, { "LawnGreen", 0x7CFC00FF}, { "LemonChiffon", 0xFFFACDFF}, { "LightBlue", 0xADD8E6FF}, { "LightCoral", 0xF08080FF}, { "LightCyan", 0xE0FFFFFF}, { "LightGoldenRodYellow", 0xFAFAD2FF}, { "LightGray", 0xD3D3D3FF}, { "LightGrey", 0xD3D3D3FF}, { "LightGreen", 0x90EE90FF}, { "LightPink", 0xFFB6C1FF}, { "LightSalmon", 0xFFA07AFF}, { "LightSeaGreen", 0x20B2AAFF}, { "LightSkyBlue", 0x87CEFAFF}, { "LightSlateGray", 0x778899FF}, { "LightSlateGrey", 0x778899FF}, { "LightSteelBlue", 0xB0C4DEFF}, { "LightYellow", 0xFFFFE0FF}, { "Lime", 0x00FF00FF}, { "LimeGreen", 0x32CD32FF}, { "Linen", 0xFAF0E6FF}, { "Magenta", 0xFF00FFFF}, { "Maroon", 0x800000FF}, { "MediumAquaMarine", 0x66CDAAFF}, { "MediumBlue", 0x0000CDFF}, { "MediumOrchid", 0xBA55D3FF}, { "MediumPurple", 0x9370D8FF}, { "MediumSeaGreen", 0x3CB371FF}, { "MediumSlateBlue", 0x7B68EEFF}, { "MediumSpringGreen", 0x00FA9AFF}, { "MediumTurquoise", 0x48D1CCFF}, { "MediumVioletRed", 0xC71585FF}, { "MidnightBlue", 0x191970FF}, { "MintCream", 0xF5FFFAFF}, { "MistyRose", 0xFFE4E1FF}, { "Moccasin", 0xFFE4B5FF}, { "NavajoWhite", 0xFFDEADFF}, { "Navy", 0x000080FF}, { "OldLace", 0xFDF5E6FF}, { "Olive", 0x808000FF}, { "OliveDrab", 0x6B8E23FF}, { "Orange", 0xFFA500FF}, { "OrangeRed", 0xFF4500FF}, { "Orchid", 0xDA70D6FF}, { "PaleGoldenRod", 0xEEE8AAFF}, { "PaleGreen", 0x98FB98FF}, { "PaleTurquoise", 0xAFEEEEFF}, { "PaleVioletRed", 0xD87093FF}, { "PapayaWhip", 0xFFEFD5FF}, { "PeachPuff", 0xFFDAB9FF}, { "Peru", 0xCD853FFF}, { "Pink", 0xFFC0CBFF}, { "Plum", 0xDDA0DDFF}, { "PowderBlue", 0xB0E0E6FF}, { "Purple", 0x800080FF}, { "Red", 0xFF0000FF}, { "RosyBrown", 0xBC8F8FFF}, { "RoyalBlue", 0x4169E1FF}, { "SaddleBrown", 0x8B4513FF}, { "Salmon", 0xFA8072FF}, { "SandyBrown", 0xF4A460FF}, { "SeaGreen", 0x2E8B57FF}, { "SeaShell", 0xFFF5EEFF}, { "Sienna", 0xA0522DFF}, { "Silver", 0xC0C0C0FF}, { "SkyBlue", 0x87CEEBFF}, { "SlateBlue", 0x6A5ACDFF}, { "SlateGray", 0x708090FF}, { "SlateGrey", 0x708090FF}, { "Snow", 0xFFFAFAFF}, { "SpringGreen", 0x00FF7FFF}, { "SteelBlue", 0x4682B4FF}, { "Tan", 0xD2B48CFF}, { "Teal", 0x008080FF}, { "Thistle", 0xD8BFD8FF}, { "Tomato", 0xFF6347FF}, { "Turquoise", 0x40E0D0FF}, { "Violet", 0xEE82EEFF}, { "Wheat", 0xF5DEB3FF}, { "White", 0xFFFFFFFF}, { "WhiteSmoke", 0xF5F5F5FF}, { "Yellow", 0xFFFF00FF}, { "YellowGreen", 0x9ACD32FF} }; const int32_t listOfColorSize = sizeof(listOfColor) / sizeof(colorList_ts); bool strnCmpNoCase(const char * input1, const char * input2, int32_t maxLen) { int32_t iii=0; while ('\0' != *input1 && '\0' != *input2 && iii < maxLen) { char in1 = *input1; char in2 = *input2; if (in1 != in2) { if (in1 <= 'Z' && in1 >= 'A') { in1 = in1 - 'A' + 'a'; } if (in2 <= 'Z' && in2 >= 'A') { in2 = in2 - 'A' + 'a'; } if (in1 != in2) { return false; } } iii++; input1++; input2++; } return true; } /** * @brief Parse a color specification from the svg file * @param[in] inputData Data C String with the xml definition * @return the parsed color */ color8_ts svg::Base::ParseColor(const char *inputData) { color8_ts localColor; localColor.red =0xFF; localColor.green=0xFF; localColor.blue =0xFF; localColor.alpha=0xFF; uint32_t red, green, blue, alpha; float fred, fgreen, fblue, falpha; size_t len = strlen(inputData); if( len >=1 && inputData[0] == '#') { if(len == 4) { if (sscanf(inputData + 1, "%1x%1x%1x", &red, &green, &blue) == 3) { localColor.red = (red | red << 4); localColor.green= (green | green << 4); localColor.blue = (blue | blue << 4); } else { SVG_ERROR(" pb in parsing the color : \"" << inputData << "\""); } } else if (len==5) { if (sscanf(inputData + 1, "%1x%1x%1x%1x", &red, &green, &blue, &alpha) == 4) { localColor.red = (red | red << 4); localColor.green = (green | green << 4); localColor.blue = (blue | blue << 4); localColor.alpha = (alpha | alpha << 4); } else { SVG_ERROR(" pb in parsing the color : \"" << inputData << "\""); } } else if (len == 7) { if (sscanf(inputData + 1, "%2x%2x%2x", &red, &green, &blue) == 3) { localColor.red = red; localColor.green= green; localColor.blue = blue; } else { SVG_ERROR(" pb in parsing the color : \"" << inputData << "\""); } } else if (len == 9) { if (sscanf(inputData + 1, "%2x%2x%2x%2x", &red, &green, &blue, &alpha) == 4) { localColor.red = red; localColor.green= green; localColor.blue = blue; localColor.alpha = alpha; } else { SVG_ERROR(" pb in parsing the color : \"" << inputData << "\""); } } else { SVG_ERROR(" pb in parsing the color : \"" << inputData << "\" ==> unknown methode ..."); } } else if( 4 <= len && inputData[0] == 'r' && inputData[1] == 'g' && inputData[2] == 'b' && inputData[3] == '(' ) { if (sscanf(inputData + 4, "%u,%u,%u,%u", &red, &green, &blue, &alpha) == 4) { localColor.red = etk_min(0xFF, red); localColor.green= etk_min(0xFF, green); localColor.blue = etk_min(0xFF, blue); localColor.alpha = etk_min(0xFF, alpha); } else if (sscanf(inputData + 4, "%u,%u,%u", &red, &green, &blue) == 3) { localColor.red = etk_min(0xFF, red); localColor.green= etk_min(0xFF, green); localColor.blue = etk_min(0xFF, blue); } else if (sscanf(inputData + 4, "%f%%,%f%%,%f%%,%f%%", &fred, &fgreen, &fblue, &falpha) == 4) { fred = etk_max(0.0, etk_min(1.0, fred)); fgreen= etk_max(0.0, etk_min(1.0, fgreen)); fblue = etk_max(0.0, etk_min(1.0, fblue)); falpha = etk_max(0.0, etk_min(1.0, falpha)); localColor.red = (uint8_t)(fred * 255.); localColor.green= (uint8_t)(fgreen * 255.); localColor.blue = (uint8_t)(fblue * 255.); localColor.alpha = (uint8_t)(falpha * 255.); } else if (sscanf(inputData + 4, "%f%%,%f%%,%f%%", &fred, &fgreen, &fblue) == 3) { fred = etk_max(0.0, etk_min(1.0, fred)); fgreen= etk_max(0.0, etk_min(1.0, fgreen)); fblue = etk_max(0.0, etk_min(1.0, fblue)); localColor.red = (uint8_t)(fred * 255.); localColor.green= (uint8_t)(fgreen * 255.); localColor.blue = (uint8_t)(fblue * 255.); } else { SVG_ERROR(" pb in parsing the color : \"" << inputData << "\" ==> unknown methode ..."); } } else if( 4 < len && inputData[0] == 'u' && inputData[1] == 'r' && inputData[2] == 'l' && inputData[3] == '(') { if (inputData[4] == '#') { // TODO : parse gradient ... } SVG_ERROR(" pb in parsing the color : \"" << inputData << "\" ==> url(XXX) is not supported now ..."); } else { bool findIt = false; // direct named color ... for (int32_t iii=0; iii> 24) ; localColor.green= (uint8_t)((0x00FF0000 & listOfColor[iii].color) >> 16) ; localColor.blue = (uint8_t)((0x0000FF00 & listOfColor[iii].color) >> 8) ; localColor.alpha = (uint8_t)(0x000000FF & listOfColor[iii].color) ; // stop searching break; } } // or color in reference ... if (findIt == false) { // TODO : Might be a named color ... need to find it ... SVG_ERROR(" pb in parsing the color : \"" << inputData << "\" ==> refs color not supported now ..."); } } SVG_VERBOSE("Parse color : \"" << inputData << "\" ==> " << localColor); return localColor; } /** * @brief Parse all the element needed in the basic node * @param[in] node standart XML node * @return true if no problem arrived */ bool svg::Base::Parse(TiXmlNode * node, agg::trans_affine& parentTrans) { SVG_ERROR("NOT IMPLEMENTED"); return false; } const char * svg::Base::SpacingDist(int32_t spacing) { static const char *tmpValue = " "; if (spacing>20) { spacing = 20; } return tmpValue + 20*4 - spacing*4; } /* void svg::Base::AggCheckChange(agg::path_storage& path, etk::VectorType &colors, etk::VectorType &pathIdx, PaintState &curentPaintProp) { if (curentPaintProp != m_paint) { SVG_INFO("add path color = " << m_paint.fill); // New color. Every new color creates new path in the path object. colors.PushBack(agg::rgba8(m_paint.fill.red, m_paint.fill.green, m_paint.fill.blue, m_paint.fill.alpha)); uint32_t tmpPathNew = path.start_new_path(); pathIdx.PushBack(tmpPathNew); curentPaintProp = m_paint; } } */