Closed IanTrudel closed 8 years ago
By the way, my initial suggestion for the API was to provide a string as a default parameter. Not sure where :from_string comes from. Maybe it is not necessary. It should look like:
svg(str, width: 200, height: 200)
:content is better than :from_string. Once someone documents rb_parse_args so I can understand it and I get comfortable with Styles parsing/handling we can get a better API. The value of an early beta is to get feedback.Thank you.
Important question @BackOrder - is OSX needed and if so, what version of OSX are you willing to accept before calling it OK. I can compile on 10.9 VM but I can not build for 10.9 from 10.10. librsvg appears to be hooked deep into /usr/lib/*.dylibs that shouldn't part of Shoes (libresolv, libc++, libc++abi and more).
It doesn't display the svg on 10.9 but that is not unexpected considering all I wanted was to build and that is not going well.
No pictures tonight. I hacked up a work around for the librsvg2.dylib dragging in libresolv.dylib. Not pretty but I can build and run Shoes OSX for 10.9 from 10.10 which was enough to get the scaled svg drawn on screen before vanishing. It appears that the VLC code (using shoes_native_surface) may never have been wired into Shoes/OSX properly so I should stop using it as a reference.
Librsvg does not support multiple svg in the file. It supports layers (or subs in their parlance. There doesn't appear to be a way to iterate through the 'subs' . You would have to find them from an XML parse? If so, you can't use the :filename and therefore your svg(str,w,h) suggestion. There is no way to test if str (a multi-KB string) is a File.path or an SVG/XML string (other than a regexp hask for the XML line). That's why there is a "content: and :filename hash argument).
I suspect the hang (osx)/crash(gtk) for the window adornment vs the quit button is that the former will may get to shoes_svg_remove() which I will describe as not written yet (see svg.c and return value);
Status update: I'm pretty confident we can get this to work. In private email, @BackOrder has provided his use cases for the facility which was a little more challenging than needed because of a dodgy svg to test with. I fixed the crash bug (svg.remove was not defined in ruby.c) and I can properly get svg layers and half ass display them.
What needs to be done next is to more closely match Shoes Image instead of basing it on the old video code. That will be another refactoring (number 3?). Will take some time as I stumble on the same bugs differently.
@BackOrder's use case was a deck of playing cards with each card in the svg file being an librsvg2 'layer'. So I tried to think like some who wants to write a card game and modify the samples script to pick layers and I came to the realization that we need to query the svg before it's shown on screen. Odds are you would want to build 'images' of all 52 cards before showing anything on screen.
It's also a fact of Shoes that it DOES NOT resize widgets or images once on screen - they may change positions as a slot expands or shrinks but they do not change their size. It's just how Shoes does it. Yes you can change the size by changing styles but there is no Shoe event you can tap into for window resizing.If you do find one, you'll regret it.
Which leads me to think that we need a a non graphical svg object (52+ of them in the card case) and a way to turn them into Images(pixels) - render them once into an Shoes::Image pixbuf and then just like any other loaded png or jpeg and would respond to Image methods. They would lose 'svg-ness' Just a chunk of pixels. Bonus points for repaint speed.
There would be a new Ruby object 'svghandle?' with the methods for querying the svg and Shoes:::slot::svg((handle, w, w) which turns that into a Image.
Comments?
yes, i think you have to one could emulate resizing with an animation, check window size and recreate image (pixels) accordingly, inside the loop, but it quickly becomes slow
Advice to developers: write the manual entry first. Then code to that. Of course I didn't do that.
I suggest adding SvgHandle class . It has no gui and should not be part of canvas. It will deal with loading and parsing the svg :content or :filename and the :subid. It would have some getters like width and height.
The Image command could allow for the first argument to be an SvgHandle (C: cSvgHandle) and accept :width and :height. It would render the svg[layer] into a pixel image, (a cairo_image_surface). After that it's just a pixbuf to hide, show, move about, draw over...
The current canvas.svg widget that renders cairo vectors could also accept the same arguments as Image.new and someday in the future it might work and be documented in the manual.
Two different ways to handle svg might be possible, Rhetorical question to get people thinking: How would you document this in the manual?
If SvgHandle is not a Ruby/Shoes subclass of cCanvas - what is it's Ruby/Shoes superclass? App? (groan). An example for thinking purposes. :
# read svg (an xml) into ' buffer'
svgdeckhandles[0,11][= app.loadsvg(:contents => buffer, :subid: => "#club_queen')
...
svgdeckhandles.each do |suite|
suite.each do |card|
@deskimage[suit', card] = image :svg => svgdeckhandles[suite,card], :widtth ->123, :
height: =>300, :visable =>false
end
end
@top_card = Image :svg=> random(deskimage ).show
I don't write games so that may not as ugly as I think .It also depends on shoes_image_cache and :visible changes to be anything near performant. Using the proposed svg widget would not help.
I've got a man month of coding into this request/branch and I'm looking at least that much more and I don't see a payout. I hate to throw away so much work but it's already a lot more time than @BackOrder complained about when converting svg to 57 png'a the manual way. No profit here.
I need some time to ponder about what you are proposing here. It is worthwhile to mention that while 57 images is not that much, some vector projects contains hundreds objects if not more.
However, I did mention that we could release the SVG feature without a complete subid support. This would be good enough for a first cycle on the feature: having a brand new SVG feature is an awesome news in itself. If you'd like to move forward with this, let's tidy up things, make sure the API makes sense, test it and wrap it up for the upcoming release. Make sense to you @ccoupe ?
It makes sense to get the Shoes api defined and then the C and Obj->C working. They are not well defined yet and the existing code has major bugs/shortcomings and I haven't gotten to OSX problems yet. Shoes/C is not amenable to changing things quickly and I'm not the sharpest knife in the drawer. We're a long ways from being tidy.
100's of svg objects - all the more reason to do it (mostly) correct the first time. I'll set the milestone back when I know what the Shoes level API /functionality/use-cases are understood and defined. Think about how you would write the svgviewer sample for that deck of cards is a helpful exercise in figuring out what the API should be.
I think we need an non graphical class for an svg or svg fragment that can be queried for sizing before the Shoes script er decides to create a Shoes svg widget (or pixbuf) for a couple of them. You need to query the svg or fragment for w&h to determine the aspect ratio before you create a graphical widget or pixbuf in Shoes.
Right. I am just saying that we can release a good SVG feature without the support for subid, or limited support. The SVG feature is working fine as far as I understand. It's the advanced features that are tricky.
We don't need a non graphical class. Do we? How about a parameter visible? Set to false would mean the SVG is not drawn on screen.
It may look good to you (try the OSX) but its just plain wrong underneath by any meme you care to hold. It shows that it can be done. Nothing more than possible. I'm sure your prototype code for clients that never appears in the final product. To help them clarify their requirements. I'm sure you have had clients that insist the prototype is perfect, ship it now and then beat the hell out of you later because you didn't read their mind.
Currently the code doesn't support Sytles because that's a lot of methods to implement and test (a lot of work), hence my half baked short cut idea to convert svg to Image pixbuf and leverage the existing image and image cache code. Creating the SvgHandles you want to render later is not is not going to be an onerous thing. Easier than writing the nokogiri query to figure out what the id's are.
This request/issue is not as simple as you think, I'm going
Why I think Svghandle class is important.
Shoes.app width: 600 do
fpath = "/home/ccoupe/Projects/shoes3/brownshoes.svg"
cpath = "/home/ccoupe/Projects/shoes3/SVG-cards-2.0.1/paris.svg"
svgh1 = svghandle({:filename => fpath})
fl = File.open(cpath,"r");
bigstring = fl.read
fl.close
svgh2 = svghandle({:content =>bigstring, :subid => '#diamond_queen'})
widget_rect = 200
flow do
stack :width => widget_rect do
w1 = svgh1.width
h1 = svgh1.height
a1 = (w1.to_f) / h1
para "w: #{w1} h: #{h1}"
para "a: #{a1}"
nw1 = widget_rect
nh1 = (widget_rect * (1 / a1)).to_i
para "nw #{nw1} nh: #{nh1}"
para "na: #{nw1.to_f / nh1}"
end
stack width: widget_rect do
w2 = svgh2.width
h2 = svgh2.height
a2 = w2.to_f / h2
nw2 = (widget_rect * a2).to_i
nh2 = widget_rect
para "w: #{w2} h: #{h2}"
para "a: #{a2}"
para "nw #{nw2} nh: #{nh2}"
para "na: #{nw2.to_f / nh2}"
end
end
end
While some scripters may know the size they have in the svg and the size they want on screen that it isn't true for any random svg. this code computes widget size constrained to the aspect ratio, so it fits properly in a 200 X 200 px, window. Yes, it screams for a better convenience method to and compute the x,y offset to to center the scale svg.
Your heart is in the right place but your approach is misguided. This doesn't fit the Shoes paradigm: it should be all-in-one class. It is noteworthy to remember Canvas class is never directly used in Shoes, if a comparison is to be made with SvgHandle. Right now I can only imagine the confusion of Shoes users over having to deal with two classes. It doesn't make sense.
Confusion? Try helping someone with Shoes and sqlite3. Or nokogiri parsing. I think the svghandle belongs in app or maybe in a an new module.. The handle is no more complex than opening a file and reading a line from the file. If they don't want to play with things, the svg call is just one simple command more. If you do want to play with things like above, it needs to be done before it gets on screen. The text_insertion point object for #144 is more complicated than older Shoes too and Shoes audience I've dealt with have been pretty knowledgeable.
It is also possible to do both. If the first arg to svg() is svghandle it would be as written. if it's a hash then get the content, filename, and the subid args and internally pass those to create the handle.
There's another potential win for using the handle. - It can be replaced w/o having to destroy/create the svg widget and triggering a full repaint. Might be useful for changing a playing card from face down to face up.
Three stacks in a flow, per example code. leftmost is drawn with svg nw1,nh1,svgh1
rightmost is drawn with svg 100, 100, {:filename => fpath}
. Center image is not drawing (subid) so I screwed up something there since it used to work. No worries.
Very cool screenshot!
The nature of both Sqlite and Nokogiri require them to have an extensive API. I am still clueless as to why SvgHandle is necessary. The features of SvgHandle seems to belong to the Svg class. One class is enough. The bottom line is that it doesn't make any sense to me. Is there something I am missing?
If you only want one method, svg - you have it now! Think of svghandle as completely optional for you then, although you did ask for dpi queries and aspect ratios, neither of which fits Shoes tradition or capabilities, Since svgs have no fixed dimensions or preferred size we can not default to the internal image values like png or jpeg files have when sizing the Image/widget for display. Shoes can't know how big or small to make the #diamond_queen. So the script writer must specify the h and w they want. With handles, they can do compute aspect. Take the simple case of paris.svg (all the cards image) - how big should that be? Defaults in the file say its larger than some screens.
Since you know the deck of cards has a 2:3 (or is that 3:2?) aspect ratio you can start working on the card game with svg, Once I fix the layer rendering bug. Code on without svghandles if you like. It will exposes weakness in the API. I think we can count on svg(w,h,hash|handle) syntax for a while. Depending on the game you choose to write you'll find out whether svghandle is a good idea for you or not. No harm done to you (or anyone else) if there is new class they don't have to use.
Thank you for explaining. There are still much I don't understand about SvgHandle but you clearly feel strongly about it.
You may have misunderstood what I mean about DPI and aspect ratio. There is no such thing as querying DPI on a vector image. Aspect ratio is defined by the existing image attributes.
My initial request is to be able to render vector images (SVG) at a specific DPI, nothing to do with querying, but instead a rendering matter. Same goes with aspect ratio where the initial aspect should be kept regardless how it is scaled or even when the width and height are not respecting the ratio (when aspect ratio is set to true only; it should use one of the value is force the other value to respect the aspect ratio).
Other software manage to compute default sizes and that's what Shoes should do.
I like the change from :subid to :layer but :group may make more sense. A layer has a different definition and can include multiple groups or objects.
'group' is an easy change to make, Maintaining the aspect ratio seems doable. If w or h changes because of the aspect changes should the image be centered or left/top anchored in the widgets rect?
I know i've played with DPI setting in the code and I couldn't see any difference, I just set it to 300 and nothing changed visually. I don't understand why it should change, either. going from 75 to 300, should it expand the picture or shrink it?
'group' is an easy change to make, Maintaining the aspect ratio seems doable. If w or h changes because of the aspect changes should the image be centered or left/top anchored in the widgets rect?
This is simple: top left corner as defined by the parameters. The width is traditionally used as the base for the aspect ratio and height is corrected. If no width is provided, height will be used to define the proper width. Alternatively, the highest value could be used to set the other parameter.
I know i've played with DPI setting in the code and I couldn't see any difference, I just set it to 300 and nothing changed visually. I don't understand why it should change, either. going from 75 to 300, should it expand the picture or shrink it?
It is a very complicated answer best explained at http://daraskolnick.com/image-dpi-web/. It has importance in complex scenarios such as when exporting image (such as when using snapshot method for a PDF or PS) or printing. Default should be 72 DPI/PPI.
DPI changes only work when rendering to gdkpixbuf and we don't have a way to save those (Image does but not this widget.) Some mimic of snapshot, I suppose. Or some way to get Image to load an svg which is the other path I could have started down and the main reason to create the SvgHandle class.
My understanding is that librsvg and cairo already provided the necessary means to define DPI. Your experience may tell you otherwise and, if so, we might postpone any kind of DPI support.
librsvg2 docs are clear (and terse) - setting dpi only effects gdkpixbuf (a raster image) and there are separate C calls to render the xml to gdkpixbuf. Cairo may well be the better place when creating a svg_drawing space since one can compute many things there. I'm still learning Cairo and confused about many things with it. For a Shoes svg API, for now I'm just going to define a svg.save(filenane, &block) - like snapshot or image &block and write the code for that much, much later.
However, http://www.cairographics.org/manual/cairo-SVG-Surfaces.html does not lead me to believe it does support anything but 72dpi.
Let's postpone DPI feature and move it to our wishlist, low priority. Thanks for clarifying.
There is a provisional svg.dpi = num and a svg.save (filename, &block) method, which do nothing useful.
I'm convinced from looking at the Image code in ruby.c that it's only blind luck that I could draw any svg so we can't be fooled by screen shots of brownshoes.svg. It demonstrates why I only hack around the edges of Shoes. I'll get it drawing properly but unless the blinding light happens, it may take some time.
Slowly (I hate pixel counting) it's getting better. Screen grab to two different test scripts.
I put a 200x200px gray background in some slots I'd have a visual clue to see it the scaling is close to correct. From the left, (1) default image - respects internal aspect, (2) aspect: "no". (3) 100x100 stack, aspect: "no. (4) respect aspect scaling up. Looking good ? not really. Add a para to the slot of (4)
The aspect calculations are a little weak but I'm way over my pay grade already. I don't think I'm drawing to the correct surface. Much head scratching ahead.
Progress! I'm feeling better about success. It was a big mistake to base the earlier design on the video widget. Now it mimics Image and it's drawing to the proper canvas. It's not drawing properly since it doesn't have the aspect and scaling code but I can add that back since it's still in the svg.c file.
Added benefit is theire is no native widget required - no Cocoa or Gtk code to write so anybody who understands cairo and C and Shoes can help, Please. Image is a big class if we have to dup and test each method and style (which is missing).
This is an interesting development. Does it have incidence on things like groups and other matters we talked about in here? Glad to see that it is now behaving more like an image, which means it will properly respect positioning, flows, etc.
It is not an Image! - It just borrows/duplicates/modifies a whole bunch of image C code. The Shoes proposed Shoes api has not changed,
Aspect is better now but x and y for group id is still a work in progress. The good news is it's clipped to the outer stack bounds - further confirmation that I'm drawing in the correct Shoes canvas. Obviously the x position (158) of the 3 of Clubs within the paris.svg file is marching to the right. cairo_translate doesn't seem to matter but I'm way over my head here.
Shoes.app width: 600 do
cpath = "/home/ccoupe/Projects/shoes3/SVG-cards-2.0.1/paris.svg"
fl = File.open(cpath,"r");
bigstring = fl.read
fl.close
#svgh2 = app.svghandle({:content =>bigstring, :group => '#heart_queen'})
svgh2 = app.svghandle({:content =>bigstring, :group => '#club_3'})
widget_rect = 200
stack do
para "One line"
flow do
stack width: 200, height: 200 do
background gray
svg 200,200,svgh2
end
stack width: 200, height: 200 do
background cyan
end
end
end
end
Four svgs, 200x200 stacks in a flow in a 700 px window. 1) respects aspects, 2) doesn't respect aspect because user told it not to, 3) one card from the paris.svg. 4) 150x150 svg asked for, respecting aspect.
If any one @passenger94 ? has a clue about getting the other card to display, please help. I suspect I need to set the surface co-ords in user space for the sub image (which is bottom/left). I'm so confused.
ok, i've synched with your repo @ccoupe, would be glad to help, if i can ... i'm looking at code now, found a deck of 52 svg cards so i can play with :-) in can render "brownshoes.svg". trying the group/id thing : in ;group option one must add "#" as a prefix for the id, right ? for example if i have :
<g
id="g6073"
in svg file, i must call app.svghandle with :group => '#g6073'
would you guide me as to where i should look first, for trying to help, meanwhile i'm working on making myself more comfortable with new code - and old code :-D -
so let see :
Shoes.app calls method shoes_svghandle_new
initialize everything, create an SvgHandle class, get dimentions and position of whole svg or subid (group) using librsvg library, gives us an svgHandle object
then we call method svg
with wanted width and height and the svgHandle object (i see there's an optional hash we can give)
this then is calling shoes_svg_new
wich create an Svg class with which user could interact, also used by Shoes for calling rendering/placement processings
the rendering is done by rsvg library directly onto cairo surface not by Shoes (not using native gtk or cocoa methods) Shoes provide placements and dimension
Am i on good tracks ?
PS : looks like we can easily hide the shoes_svghandle_new
prelude, let method svg
call it under the hood
svg(filename, group, width, height,...) ==> call shoes_svghandle_new
, get an svgHandle, store it in shoes_svg structure ... ??
like : either svg(filename, width, height) or svg(filename, options_hash)
Thats a pretty good summary. The svghandle is mostly optional - the manual documents it. here's the test for the problematic code
Shoes.app width: 600 do
cpath = "/home/ccoupe/Projects/shoes3/SVG-cards-2.0.1/paris.svg"
fl = File.open(cpath,"r");
bigstring = fl.read
fl.close
#svgh2 = app.svghandle({:content =>bigstring, :group => '#heart_queen'})
svgh2 = app.svghandle({:content =>bigstring, :group => '#club_2'})
widget_rect = 200
stack do
para "One line"
flow do
stack width: 200, height: 200 do
background gray
svg 200,200,svgh2
end
stack width: 200, height: 200 do
background cyan
end
end
end
end
Lines 237 through 240 of svg.c are the problem for drawing a group/subid.
What is the specific problem in those lines ? Main problem for me now is that i can't see a group that is too far away from the origin of given svg file (as reported by svghpos.x and svghpos.y), i'm trying to understand where in placement processing we could introduce corrections so we get the right offset Also aspect calculations seems weird, maybe it's just me not at ease with it ?
we probably need to suspend shoes_svg_draw method when not necessary, only on hide/show operations for example
he he, right where you said ! line 237 we need to scale first then translate (idem for regular svg -not a group-) and substracting the offset in cairo_translate
cairo_translate(cr, place->ix + place->dx, place->iy + place->dy);
cairo_scale(cr, scalew, scaleh);
becomes :
cairo_scale(cr, scalew, scaleh);
cairo_translate(cr, place->ix + place->dx - svghan->svghpos.x, place->iy + place->dy - svghan->svghpos.y);
now regular svg needs a fix !
Not heavily tested ! could be a bad idea ... offset looks good, scaling is acting weird on some numbers ?
a proposal fix for shoes_svg_draw_surface fixed aspect ratio calculus Needs testing
static void
shoes_svg_draw_surface(cairo_t *cr, shoes_svg *self_t, shoes_place *place, cairo_surface_t *surf, int imw, int imh)
{
shoes_svghandle *svghan;
Data_Get_Struct(self_t->svghandle, shoes_svghandle, svghan);
double outw = imw * 1.0;
double outh = imh * 1.0;
double scalew = outw / svghan->svghdim.width;
double scaleh = outh / svghan->svghdim.height;
if (svghan->aspect != 1.0)
{
scalew = scaleh = MIN(outw / svghan->svghdim.width, outh / svghan->svghdim.height);
}
cairo_scale(cr, scalew, scaleh);
if (svghan->subid == NULL)
{
// Full svg contents
// if (place->iw != imw || place->ih != imh) // What's the use of this ?
// cairo_scale(cr, (place->iw * 1.) / imw, (place->ih * 1.) / imh); //
cairo_translate(cr, place->ix + place->dx, place->iy + place->dy);
cairo_set_source_surface(cr, surf, 0., 0.);
rsvg_handle_render_cairo_sub(svghan->handle, cr, svghan->subid);
self_t->place = *place;
}
else
{
cairo_translate(cr, place->ix + place->dx - svghan->svghpos.x, place->iy + place->dy - svghan->svghpos.y);
cairo_set_source_surface(cr, surf, 0., 0.);
rsvg_handle_render_cairo_sub(svghan->handle, cr, svghan->subid);
self_t->place = *place;
}
printf("surface\n");
}
Excellent. I knew I didn't know what I was doing. Thank you!
Tested with :
Shoes.app width: 600 do
#cpath = "#{DIR}/Hqueen_simple.svg"
cpath = "#{DIR}/Color_52_Faces_simple.svg"
bigstring = ""
File.open(cpath,"r") {|fl| bigstring = fl.read }
#svgh2 = app.svghandle({:content =>bigstring, :group => '#heart_queen'})
svgh2 = app.svghandle({:content =>bigstring, :group => '#g6073'})
stack do
para "One line"
flow do
stack width: 400, height: 400 do
background gray
svg 400,400, svgh2
end
stack width: 200, height: 200 do
background cyan
svg 200, 200, svgh2
end
end
end
end
:-))
Did @passenger94 single-handedly fixed the group positioning? :100:
;-D Thanks lots have been done !!!! much easier to come late and fix some quirks !! ... and still lots need to be done
Beware though my fix always maintains the aspect ratio of the original svg if we want to allow changing aspect ratio, there's still some work to do here, but do we want that ? if not we need to change the way we call svghandle method, i think it might be a good idea to hide that method anyway
Aspect ratio is a feature, not mandatory.There are many reasons one would prefer to keep, or not, aspect ratio.
the handle exists mostly so the code can be reused by the Real Image class. - given how it deals with its arguments. It should be possible to render an svg to a gdk-pixbuf and load that into a real Shoes Image. For simple svgs and ops, that wouldn't be much different on screen. However, that's a raster and Svg widget is not a raster. Does any body want to work on getting styles parse in svg.new?
Speaking of styles parsing, how do you want aspect ratio to be specified by the user ?
in any case without the param, assume keep aspect ratio maybe change the name for keep_aspect_ratio, keep_AR ... or adapt_widget, adapt_svg ... adapt_parent ?
In regard to aspect ratio:
All reasonable things to do or add, including hiding or not using svghandle. I'm only committed to ideas that work.
I built Shoes on OSX and once you can find the error messages (never easy in OSX) , cairo is complaining mightily about surface sizes (and not displaying anything). I need to fix that before going too far.
Also, gtk complains about non-widgets when Shoes closes down (triggered from canvas.c:1224 and a quick looks suggests the Svg widget is still not wired up correctly. Need to fix that. Those are independent from Styles and method/arg names and parse so if @BackOrder or @passenger94 want to work on the naming and parsing, I'm good with what ever you come up. Probably. Working code always beats good ideals.
Once all three of those are done, then I'd like to merge the svg branch back into master because I don't like long running branches, even if the svg code is missing some methods.
Cairo supports SVG rendering through libRSVG.
https://wiki.gnome.org/action/show/Projects/LibRsvg?action=show&redirect=LibRsvg
From http://cairographics.org/examples/