Is it possible to detect unique colours inside an SVG? - svg

I want to access all the colours that an SVG may be using. I've played with convert, but would like some direction on how to figure out the unique colours that an SVG may contain.
This is the PHP code I've written to talk to convert, to try and determine whether a bitmap contains colours, however it is very limited:
/**
* containsColor
*
* #see https://www.imagemagick.org/discourse-server/viewtopic.php?t=19580&start=15
* #see https://superuser.com/questions/508472/how-to-recognize-black-and-white-images
* #see https://www.imagemagick.org/discourse-server/viewtopic.php?t=19580
* #access public
* #static
* #param string $path
* #return bool
*/
public static function containsColor(string $path): bool
{
$commands = array(
'convert ' .
($path) .' ' .
'-format "%[colorspace]" info:'
);
$command = implode(' && ', $commands);
$response = exec($command);
$color = strtolower($response) !== 'gray';
return $color;
}

If we review Format and Print Image Properties document, we should be able to identify the unique color count with -format %k.
public static function containsColor(string $path): int
{
$commands = array(
'convert ' .
($path) .' ' .
'-format "%k" info:'
);
$command = implode(' && ', $commands);
$response = exec($command);
return (int)$response;
}
If you want to evaluate all colors used by the SVG post render, you can use -unique-colors operator.
convert input.svg -depth 8 -unique-colors txt:-
Which will output something that can easily be parsed by PHP.
# ImageMagick pixel enumeration: 403,1,65535,srgba
0,0: (0,0,0,65535) #000000FF black
1,0: (257,0,0,65535) #010000FF srgba(1,0,0,1)
2,0: (257,257,257,65535) #010101FF srgba(1,1,1,1)
3,0: (257,257,0,65535) #010100FF srgba(1,1,0,1)
4,0: (514,257,0,65535) #020100FF srgba(2,1,0,1)
5,0: (771,771,771,65535) #030303FF grey1
...
Remember that SVG is really just XML, so it may be possible to load it into a DOMDocument class, and use DOMXPath to extract color attributes. But as correctly mentioned in the comments, you would not be able to identify CSS3 filters, or advanced color rendering & mixing.
Update with working example
public static function containsColor(string $input) : bool
{
$pixelInfoList = [];
exec(sprintf('convert "%s" -depth 8 -alpha Off --colors 255 -unique-colors txt:-', $input), $pixelInfoList);
// ... Insert error handling here ...
for($index = 1; $index < count($pixelInfoList); $index++) {
preg_match('/\((\d+),(\d+),(\d+)\)/', $pixelInfoList[$index], $colorParts);
if ($colorParts[1] == $colorParts[2] && $colorParts[2] == $colorParts[3]) {
// Color is gray. Do nothing?
} else {
// Non-gray color. Stop search, and return.
return true;
}
}
return false;
}
Not perfect, but a start.
This works be evaluating the color channels outputted by txt:-. If the red, green, and blue channels are the same, we can state it's a gray color & continue to the next line, else we can state a non-gray color exists & stop the iterations. I'm also taking the library of using -alpha Off -colors 255 to through out additional data.
YMMV

This probably won't help you, because you seem to want to use this for a server application, but it might help others with the same question.
If you're using Inkscape, you can also just export as .gpl.
File > Save a Copy : GIMP Palette (*.gpl)
This will, however, not save all the colors that are displayed within a gradient, only those that are actually used in the file.

Related

Typo3: GIFBUILDER add distorted text on top of image

I want to open a blank catalog image and apply on top of it some skewed (distorted) text.
I have this typoscript snippet:
/**
* Catalog image
*/
lib.catalogImage = IMAGE
lib.catalogImage {
file = GIFBUILDER
file {
XY = 242,270
format = png
quality = 80
10 = IMAGE
10 {
file = EXT:theme/Resources/Public/Images/catalog_blank.jpg
}
15 = IMAGE
15 {
offset = 20,20
file = GIFBUILDER
file {
XY = 150,50
10 = TEXT
10 {
text = Datenblatt
fontSize = 12
offset = 28,110
fontColor = black
niceText = 1
}
20 = SCALE
20 {
params = -matte -virtual-pixel transparent -distort Perspective '0,0,0,0 0,90,0,90 90,0,90,25 90,90,90,65'
}
}
}
}
}
The problem is, I always get the text box white, with no text in it.
What am I doing wrong?
Blank catalog image:
Result image:
EDIT:
I've also tried adding:
niceText.after = -matte -virtual-pixel transparent -distort Perspective '0,0,0,0 0,90,0,90 90,0,90,25 90,90,90,65'
to the TEXT object, according to the docs (https://docs.typo3.org/typo3cms/TyposcriptReference/Gifbuilder/ObjectNames/Index.html), but it doesn't seem to be considered at all.
The result is this:
It looks like niceText doesn't get along with SCALE.
I removed niceText and the text is now displayed correctly.

Test for Label overrun

I have a multiline label whose text has a chance of overrunning. If it does this, I want to decrease the font size until it isn't overrunning, or until it hits some minimum size. This would hopefully make it so that the label will change size until the entire string is visible.
My problem it that I am not sure how to test to see if the text has overrun. I have tried testing to see if the label's text ends with the ellipse string, but I believe the ellipse is not technically added to the textProperty of the label. So does anyone know of a good way to test for this?
The short and disappointing answer: You simply cannot do this in a reliable way.
The slightly longer answer is, that the label itself does not even know whether it's overflown or not. Whenever a label is resized, the skin class (LabeledSkinBase) is responsible for updating the displayed text. This class, however, uses a JavaFX utils class to compute the ellipsoided text. The problem here is that the respective method just returns a string that is ellipsoid if this is required by the label's dimensions. The skin itself never gets informed about whether the text was actually ellipsoided or not, it just updates the displayed text to the returned result.
What you could try is to check the displayed text of the skin class, but it's protected. So you would need to do is to subclass LabelSkin, and implement something like that:
package com.sun.javafx.scene.control.skin;
import java.lang.reflect.Field;
import javafx.scene.control.Label;
#SuppressWarnings("restriction")
public class TestLabel extends LabelSkin {
private LabeledText labelledText;
public TestLabel(Label label) throws Exception {
super(label);
for (Field field : LabeledSkinBase.class.getDeclaredFields()) {
if (field.getName().equals("text")) {
field.setAccessible(true);
labelledText = (LabeledText) field.get(this);
break;
}
}
}
public boolean isEllipsoided() {
return labelledText != null && labelledText.getText() != null && !getSkinnable().getText().equals(labelledText.getText());
}
}
If you use this skin for you Label, you should be able to detect whether your text is ellipsoided. If you wonder about the loop and the reflection: Java didn't allow me to access the text field by other means, so this may be a strong indicator that you really should not do this ;-) Nevertheless: It works!
Disclaimer: I've only checked for JavaFX 8
minisu posted a way to detect an overrun in this answer:
https://stackoverflow.com/a/15178908/9492864
This way works for all labeled and I tested it on Buttons with JavaFX 8.
You can add a listener for example to the needsLayoutProperty:
labeled.needsLayoutProperty().addListener((observable, oldValue, newValue) -> {
String originalString = labeled.getText();
Text textNode = (Text) labeled.lookup(".text"); // "text" is the style class of Text
String actualString = textNode.getText();
boolean clipped = !actualString.isEmpty() && !originalString.equals(actualString);
System.out.println("is " + originalString + " clipped: " + clipped);
});
This work for me !!
In javafx
public class LabelHelper{
public static boolean necesitaTooltip(Font font, Label label){
Text text = new Text(label.getText());
text.setFont(font);
Bounds tb = text.getBoundsInLocal();
Rectangle stencil = new Rectangle(
tb.getMinX(), tb.getMinY(), tb.getWidth(), tb.getHeight()
);
Shape intersection = Shape.intersect(text, stencil);
Bounds ib = intersection.getBoundsInLocal();
return ib.getWidth() > label.getPrefWidth();
}
public static void asignarTexto(Label label, String texto){
label.setText(texto);
if (necesitaTooltip(label.getFont(), label)){
Tooltip tp = new Tooltip(texto);
label.setTooltip(tp);
}
}
}
Only call a asignarTexto(label, texto) for set text a label and check if the text is overrun in the label then add a tooltip for label.
It's already been mentioned by Johann that you can use (Text)labeled.lookup(".text") to get the actual displayed text for a Label, then compare it to the intended String... However, in my case, this did not work. Perhaps it was because I was updating the Label with a high frequency, but the actual String was always a few chars less than the intended...
So, I opted to use the setEllipsisString(String value) method to set the ellipsis string (what's appended to the end of a Label when there's overrun, the default being "...") to an (unused)
ASCII control character like 0x03 (appropriately named "end of text"), then after each time I set the Label text I check if the last char of the actual String is the control char.
Example using Platform.runLater(Runnable):
import javafx.scene.control.Label;
import javafx.application.Platform;
import javafx.scene.text.Text;
...
Label label = new Label();
...
label.setEllipsisString("\003");
...
final String newText = "fef foo";
Platform.runLater(() -> {
label.setText(newText);
String actual = ((Text)label.lookup(".text")).getText();
// \003 is octal for the aforementioned "end of text" control char
if (actual.length() > 0 && actual.charAt(actual.length()-1) == '\003') {
// Handle text now that you know it's clipped
}
});
Note that you can set the control char to anything really, and it doesn't need to be just one char; however if you opt for a control character, check that it isn't commonly used.

Javafx change PieChart color

I have a list of color representing a color sequence. I want to apply the new color sequence to the piechart data.
private final int CASPIAN_COLOR_COUNTS = 8;
public void setPieChartColor(PieChart chart, List<String> colors) {
chart.getData().get(i); // for debug to get the node name (.data)
/**
* Set Pie color
*/
int i = 0;
for (String color : colors) {
final Node node = chart.lookup(".data" + i);
node.getStyleClass().remove("default-color" + (i % CASPIAN_COLOR_COUNTS));
node.getStyleClass().add(color);
i++;
}
but all chart data take Only one color from Caspian color.
You can achieve custom pie colors in code using a method such as:
private void applyCustomColorSequence(
ObservableList<PieChart.Data> pieChartData,
String... pieColors) {
int i = 0;
for (PieChart.Data data : pieChartData) {
data.getNode().setStyle(
"-fx-pie-color: " + pieColors[i % pieColors.length] + ";"
);
i++;
}
}
Note that the method must be applied after the chart has been shown on an active scene (otherwise the data.getNode() call will return null).
Here is some sample code which uses it.
You can accomplish the same effect using css stylesheets.
For example a css stylesheet containing the following style definitions will change the default colors of a pie chart when the stylesheet is applied against a given chart.
.default-color0.chart-pie { -fx-pie-color: #ffd700; }
.default-color1.chart-pie { -fx-pie-color: #ffa500; }
.default-color2.chart-pie { -fx-pie-color: #860061; }
.default-color3.chart-pie { -fx-pie-color: #adff2f; }
.default-color4.chart-pie { -fx-pie-color: #ff5700; }
For an example of the stylesheet based approach: see the "Setting Colors of a Pie Chart" section of the Styling Charts with CSS tutorial.
The stylesheet approach has an advantage that the styles are separated from the code. It has the disadvantage that the colors are must be set the time the stylesheet are created rather than at runtime and the color sequence is restricted to a fixed number of colors (8).
In general, the stylesheet approach is recommended for most applications.
The css styles might not work if your values are negative. This might be a bug but I had to convert my values to positive values for the styles to work. When the values were negative all pies were white in color.

PerlTk canvas + pixel manipulation

I'm having some problems understanding how the image types in PerlTk work.
I basically want a way to create an "empty" image (for example fully white) and then manipulate the pixel data. I need to able to change a pixel to black or white.
I've tried a few approaches but non of them seem to work. Documentation seems to be pretty scarce and very old. This is closest I've got.
#args name, width, height, data
my $bitmap = $mw->DefineBitmap('cells', 1, 1, pack("b1", "1"));
#args x-pos, y-pos, bitmap-name
$canvas->createBitmap(0, 0, -bitmap => 'cells');
Another idea I had was to use a Photo element but I couldn't find any documentation on how to create one with the "-data" option.
Any help is appreciated.
Use the put() method if you have to set single pixels. Here's a complete example:
use Tk;
my $mw = tkinit;
my $p = $mw->Photo(-width => 100, height => 100);
my $l = $mw->Label(-image => $p, -border => 0)->pack;
for (0..99) {
$p->put("#000000", -to => $_,$_);
$p->put("#000000", -to => 100-$_,$_);
}
MainLoop;

How to display vertical text (90 degree rotated) in all browsers?

How can I display vertical text (90 degree rotated) in all browsers?
(source: sun.com)
The problem is independent from the server side language. If it's not a problem when the vertically rendered text isn't text anymore but an image, choose the solution provided by tharkun. Otherwise, there are ways to do it in the presentation layer.
First, there's (at the moment) an IE-only solution, which is part of the CSS3 standard. You can check it live.
p {
writing-mode: tb-rl;
}
The CSS3 text module also specify some properties for text orientation.
Other guys do it with SVG.
I don't think you can rotate text with PHP/HTML/CSS. But you can create an image with GD containing vertical text.
Example:
header ("Content-type: image/png");
// imagecreate (x width, y width)
$img_handle = #imagecreatetruecolor (15, 220) or die ("Cannot Create image");
// ImageColorAllocate (image, red, green, blue)
$back_color = ImageColorAllocate ($img_handle, 0, 0, 0);
$txt_color = ImageColorAllocate ($img_handle, 255, 255, 255);
ImageStringUp ($img_handle, 2, 1, 215, $_GET['text'], $txt_color);
ImagePng ($img_handle);
ImageDestroy($img_handle);
function verticletext($string)
{
$tlen = strlen($string);
for($i=0;$i<$tlen;$i++)
{
$vtext .= substr($string,$i,1)."<br />";
}
return $vtext;
}
there you go no echo
Text rotation is also possible with other browsers.
CSS:
/*Safari*/
-webkit-transform: rotate(-90deg);
/*Firefox*/
-moz-transform: rotate(-90deg);
/*Opera*/
-o-transform: rotate(-90deg);
/*IE*/
writing-mode: tb-rl;
filter: flipV flipH;
I use the function below if table header rows are too long. It's quite useful because it's easy to use, fast and you don't have to calculate text height & width. Those css-gimmicks just don't work.
#######################################################
# convert text to image and embed it to html
# uses /tmp as a cache to make it faster
# usage: print imagetext("Hello my friend");
# Created by Ville Jungman, GPL-licenced, donated by www.varuste.net
function imagetext($text,$size = 10,$color = array(253,128,46)){
$dir = "/tmp/tekstit";
$filename = "$dir/" . base64_encode($text);
if(!file_exists($filename)){
$font = "/usr/share/fonts/truetype/freefont/FreeSans.ttf";
$box = imagettfbbox($size,90,$font,$text);
$w = -$box[4] - 1;
$h = -$box[3];
$im = imagecreatetruecolor($w,$h);
$white = imagecolorallocate($im,$color[1],$color[2],$color[3]);
$black = imagecolorallocate($im, 0x00, 0x00, 0x00);
imagecolortransparent($im,$white);
imagefilledrectangle($im, 0, 0, $size, 99, $white);
imagettftext($im,$size,90,$size,$h,$black,$font,$text);
#mkdir($dir);
imagepng($im,$filename);
imagedestroy($im);
}
$data = base64_encode(file_get_contents($filename));
return "<img src='data:image/png;base64,$data'>";
}
This thread suggests that you can write text to an image and then rotate the image.
It appears to be possible with IE but not with other browsers so it might be one of those little win for IE6 =)
imagettftext oughta do the trick.
As far as I know it's not possible to get vertical text with CSS, so that means that the rotated text has to be in an image. It's very straightforward to generate with PHP's' libgd interface to output an image file.
Note however that this means using one script to produce the image, and another to produce the surrounding web page. You can't generally (inline data: URI's notwithstanding) have one script produce more than one page component.
Use raphaeljs
It works on IE 6 also
http://raphaeljs.com/text-rotation.html
function verticletext($string)
{
$tlen = strlen($string);
for($i=0;$i<$tlen;$i++)
{
echo substr($string,$i,1)."<br />";
}
}

Resources