|
3 | 3 | #include "node.hpp"
|
4 | 4 | #include "extend.hpp"
|
5 | 5 | #include "to_string.hpp"
|
| 6 | +#include "color_maps.hpp" |
6 | 7 | #include <set>
|
| 8 | +#include <iomanip> |
7 | 9 | #include <algorithm>
|
8 | 10 | #include <iostream>
|
9 | 11 |
|
@@ -1352,5 +1354,236 @@ namespace Sass {
|
1352 | 1354 | else { return &sass_null; }
|
1353 | 1355 | }
|
1354 | 1356 |
|
| 1357 | + string Map::to_string(bool compressed, int precision) const |
| 1358 | + { |
| 1359 | + string res(""); |
| 1360 | + if (empty()) return res; |
| 1361 | + if (is_invisible()) return res; |
| 1362 | + bool items_output = false; |
| 1363 | + for (auto key : keys()) { |
| 1364 | + if (key->is_invisible()) continue; |
| 1365 | + if (at(key)->is_invisible()) continue; |
| 1366 | + if (items_output) res += compressed ? "," : ", "; |
| 1367 | + Value* v_key = dynamic_cast<Value*>(key); |
| 1368 | + Value* v_val = dynamic_cast<Value*>(at(key)); |
| 1369 | + if (v_key) res += v_key->to_string(compressed, precision); |
| 1370 | + res += compressed ? ":" : ": "; |
| 1371 | + if (v_val) res += v_val->to_string(compressed, precision); |
| 1372 | + items_output = true; |
| 1373 | + } |
| 1374 | + return res; |
| 1375 | + } |
| 1376 | + |
| 1377 | + string List::to_string(bool compressed, int precision) const |
| 1378 | + { |
| 1379 | + string res(""); |
| 1380 | + if (empty()) return res; |
| 1381 | + if (is_invisible()) return res; |
| 1382 | + bool items_output = false; |
| 1383 | + string sep = separator() == SASS_COMMA ? "," : " "; |
| 1384 | + if (!compressed && sep == ",") sep += " "; |
| 1385 | + for (size_t i = 0, L = size(); i < L; ++i) { |
| 1386 | + Expression* item = (*this)[i]; |
| 1387 | + if (item->is_invisible()) continue; |
| 1388 | + if (items_output) res += sep; |
| 1389 | + if (Value* v_val = dynamic_cast<Value*>(item)) |
| 1390 | + { res += v_val->to_string(compressed, precision); } |
| 1391 | + items_output = true; |
| 1392 | + } |
| 1393 | + return res; |
| 1394 | + } |
| 1395 | + |
| 1396 | + string String_Schema::to_string(bool compressed, int precision) const |
| 1397 | + { |
| 1398 | + string res(""); |
| 1399 | + for (size_t i = 0, L = length(); i < L; ++i) { |
| 1400 | + if ((*this)[i]->is_interpolant()) res += "#{"; |
| 1401 | + if (Value* val = dynamic_cast<Value*>((*this)[i])) |
| 1402 | + { res += val->to_string(compressed, precision); } |
| 1403 | + if ((*this)[i]->is_interpolant()) res += "}"; |
| 1404 | + } |
| 1405 | + return res; |
| 1406 | + } |
| 1407 | + |
| 1408 | + string Null::to_string(bool compressed, int precision) const |
| 1409 | + { |
| 1410 | + return "null"; |
| 1411 | + } |
| 1412 | + |
| 1413 | + string Boolean::to_string(bool compressed, int precision) const |
| 1414 | + { |
| 1415 | + return value_ ? "true" : "false"; |
| 1416 | + } |
| 1417 | + |
| 1418 | + // helper function for serializing colors |
| 1419 | + template <size_t range> |
| 1420 | + static double cap_channel(double c) { |
| 1421 | + if (c > range) return range; |
| 1422 | + else if (c < 0) return 0; |
| 1423 | + else return c; |
| 1424 | + } |
| 1425 | + |
| 1426 | + string Color::to_string(bool compressed, int precision) const |
| 1427 | + { |
| 1428 | + stringstream ss; |
| 1429 | + |
| 1430 | + // original color name |
| 1431 | + // maybe an unknown token |
| 1432 | + string name = disp(); |
| 1433 | + |
| 1434 | + // resolved color |
| 1435 | + string res_name = name; |
| 1436 | + |
| 1437 | + double r = round(cap_channel<0xff>(r_)); |
| 1438 | + double g = round(cap_channel<0xff>(g_)); |
| 1439 | + double b = round(cap_channel<0xff>(b_)); |
| 1440 | + double a = cap_channel<1> (a_); |
| 1441 | + |
| 1442 | + // get color from given name (if one was given at all) |
| 1443 | + if (name != "" && names_to_colors.count(name)) { |
| 1444 | + Color* n = names_to_colors.find(name)->second; |
| 1445 | + r = round(cap_channel<0xff>(n->r())); |
| 1446 | + g = round(cap_channel<0xff>(n->g())); |
| 1447 | + b = round(cap_channel<0xff>(n->b())); |
| 1448 | + a = cap_channel<1> (n->a()); |
| 1449 | + } |
| 1450 | + // otherwise get the possible resolved color name |
| 1451 | + else { |
| 1452 | + int numval = static_cast<int>(r) * 0x10000 + static_cast<int>(g) * 0x100 + static_cast<int>(b); |
| 1453 | + if (colors_to_names.count(numval)) |
| 1454 | + res_name = colors_to_names.find(numval)->second; |
| 1455 | + } |
| 1456 | + |
| 1457 | + stringstream hexlet; |
| 1458 | + hexlet << '#' << setw(1) << setfill('0'); |
| 1459 | + // create a short color hexlet if there is any need for it |
| 1460 | + if (compressed && is_color_doublet(r, g, b) && a == 1) { |
| 1461 | + hexlet << hex << setw(1) << (static_cast<unsigned long>(r) >> 4); |
| 1462 | + hexlet << hex << setw(1) << (static_cast<unsigned long>(g) >> 4); |
| 1463 | + hexlet << hex << setw(1) << (static_cast<unsigned long>(b) >> 4); |
| 1464 | + } else { |
| 1465 | + hexlet << hex << setw(2) << static_cast<unsigned long>(r); |
| 1466 | + hexlet << hex << setw(2) << static_cast<unsigned long>(g); |
| 1467 | + hexlet << hex << setw(2) << static_cast<unsigned long>(b); |
| 1468 | + } |
| 1469 | + |
| 1470 | + if (compressed && !this->is_delayed()) name = ""; |
| 1471 | + |
| 1472 | + // retain the originally specified color definition if unchanged |
| 1473 | + if (name != "") { |
| 1474 | + ss << name; |
| 1475 | + } |
| 1476 | + else if (r == 0 && g == 0 && b == 0 && a == 0) { |
| 1477 | + ss << "transparent"; |
| 1478 | + } |
| 1479 | + else if (a >= 1) { |
| 1480 | + if (res_name != "") { |
| 1481 | + if (compressed && hexlet.str().size() < res_name.size()) { |
| 1482 | + ss << hexlet.str(); |
| 1483 | + } else { |
| 1484 | + ss << res_name; |
| 1485 | + } |
| 1486 | + } |
| 1487 | + else { |
| 1488 | + ss << hexlet.str(); |
| 1489 | + } |
| 1490 | + } |
| 1491 | + else { |
| 1492 | + ss << "rgba("; |
| 1493 | + ss << static_cast<unsigned long>(r) << ","; |
| 1494 | + if (!compressed) ss << " "; |
| 1495 | + ss << static_cast<unsigned long>(g) << ","; |
| 1496 | + if (!compressed) ss << " "; |
| 1497 | + ss << static_cast<unsigned long>(b) << ","; |
| 1498 | + if (!compressed) ss << " "; |
| 1499 | + ss << a << ')'; |
| 1500 | + } |
| 1501 | + |
| 1502 | + return ss.str(); |
| 1503 | + |
| 1504 | + } |
| 1505 | + |
| 1506 | + string Number::to_string(bool compressed, int precision) const |
| 1507 | + { |
| 1508 | + |
| 1509 | + string res; |
| 1510 | + |
| 1511 | + // check if the fractional part of the value equals to zero |
| 1512 | + // neat trick from http://stackoverflow.com/a/1521682/1550314 |
| 1513 | + // double int_part; bool is_int = modf(value, &int_part) == 0.0; |
| 1514 | + |
| 1515 | + // this all cannot be done with one run only, since fixed |
| 1516 | + // output differs from normal output and regular output |
| 1517 | + // can contain scientific notation which we do not want! |
| 1518 | + |
| 1519 | + // first sample |
| 1520 | + stringstream ss; |
| 1521 | + ss.precision(12); |
| 1522 | + ss << value_; |
| 1523 | + |
| 1524 | + // check if we got scientific notation in result |
| 1525 | + if (ss.str().find_first_of("e") != string::npos) { |
| 1526 | + ss.clear(); ss.str(string()); |
| 1527 | + ss.precision(max(12, precision)); |
| 1528 | + ss << fixed << value_; |
| 1529 | + } |
| 1530 | + |
| 1531 | + string tmp = ss.str(); |
| 1532 | + size_t pos_point = tmp.find_first_of(".,"); |
| 1533 | + size_t pos_fract = tmp.find_last_not_of("0"); |
| 1534 | + bool is_int = pos_point == pos_fract || |
| 1535 | + pos_point == string::npos; |
| 1536 | + |
| 1537 | + // reset stream for another run |
| 1538 | + ss.clear(); ss.str(string()); |
| 1539 | + |
| 1540 | + // take a shortcut for integers |
| 1541 | + if (is_int) |
| 1542 | + { |
| 1543 | + ss.precision(0); |
| 1544 | + ss << fixed << value_; |
| 1545 | + res = string(ss.str()); |
| 1546 | + } |
| 1547 | + // process floats |
| 1548 | + else |
| 1549 | + { |
| 1550 | + // do we have have too much precision? |
| 1551 | + if (pos_fract < precision + pos_point) |
| 1552 | + { precision = pos_fract - pos_point; } |
| 1553 | + // round value again |
| 1554 | + ss.precision(precision); |
| 1555 | + ss << fixed << value_; |
| 1556 | + res = string(ss.str()); |
| 1557 | + // maybe we truncated up to decimal point |
| 1558 | + size_t pos = res.find_last_not_of("0"); |
| 1559 | + bool at_dec_point = res[pos] == '.' || |
| 1560 | + res[pos] == ','; |
| 1561 | + // don't leave a blank point |
| 1562 | + if (at_dec_point) ++ pos; |
| 1563 | + res.resize (pos + 1); |
| 1564 | + } |
| 1565 | + |
| 1566 | + // some final cosmetics |
| 1567 | + if (res == "-0.0") res.erase(0, 1); |
| 1568 | + else if (res == "-0") res.erase(0, 1); |
| 1569 | + |
| 1570 | + // add unit now |
| 1571 | + res += unit(); |
| 1572 | + |
| 1573 | + // and return |
| 1574 | + return res; |
| 1575 | + |
| 1576 | + } |
| 1577 | + |
| 1578 | + string String_Quoted::to_string(bool compressed, int precision) const |
| 1579 | + { |
| 1580 | + return quote_mark_ ? quote(value_, quote_mark_, true) : value_; |
| 1581 | + } |
| 1582 | + |
| 1583 | + string String_Constant::to_string(bool compressed, int precision) const |
| 1584 | + { |
| 1585 | + return quote_mark_ ? quote(value_, quote_mark_, true) : value_; |
| 1586 | + } |
| 1587 | + |
1355 | 1588 | }
|
1356 | 1589 |
|
0 commit comments