Code krpano

Compress XML for your krpano projects.

Today I wrote a little script to remove unnescessary bytes from an xml. Feel free to make it even better.

Create a file called tourbuilder.php and paste this code inside of it

class Tourbuilder{
    public function compressXML($output){
        //strip comments
        $output = preg_replace('/<!--(.*?)-->/', '', $output);
        //strip tabs, double spaces and newlines
        $output = preg_replace("/\s+/S", " ", $output);
        //strip spaces between tags
        $output = str_replace("> <", "><", $output);
        return $output;
    public function compressXMLFile($filepath){
        $file = file_get_contents($filepath);
        return $this->compressXML($file);

Next, create file called parser.php and paste this code:

    #load the tourbuilder class
    $builder = new Tourbuilder();
    #set xml headers so the browser recognizes this as xml
    header ("Content-Type:text/xml");
    header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
    header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
    echo $builder->compressXMLFile('tour.xml');
    #you could echo another xml if you want to

Now copy paste those two files in your tour directory and make sure you have the correct path to the xml set. if tour.xml doesn’t work, try dirname(‘tour.xml’) or complete path. Make sure there is no code echoed or printed before the headers. And finally make sure your server supports file_get_contents. Some (cheap) shared hosting disallow it (You could always setup a local server using WAMP or LAMP).

Next redirect your browser to If everything is setup ok, you’ll see a compressed xml (use view source, since some browser auto indent the code). Now you can save the xml, and upload it (If there is enough animo, I’ll create an option so it will save automatically to tour.min.xml or something).

Never link the parser, as it will generate the xml every visit. This is slow and the user has to redownload the xml everytime (since its not cached). It’s purely made to generate an xml once, and then save it.

krpano News


A few days ago, Klaus released krpano It was supposed to be a small bugfix version of, but Klaus keeps amazing us all by adding tons of nice features.

I won’t discuss every feature, but I’ll list a few that caught my eye.

has been renamed toto prevent confusion (still works though). Since’s are not always plugins, but more often just layers with graphics or placeholders, Klaus imagined renaming would help comprehending the massive api of krpano.

The parent system is revamped. You now have layers that are of the type “container” and within those layers you can have sublayers. You now structure your plugins/layers on a more natural (xml) way. A nice addittion to this is, are two new properties, bgcolor and bgalpha. Without the need of an image, you can now create interface elements.

<layer name="links" type="container" keep="true" align="righttop" width="170" height="170" x="10" style="gui_fade_style">
	<layer name="link_1" type="container" bgcolor="0x5d1824" bgalpha="1"  align="lefttop" width="85" height="85" x="85" y="0" scalechildren="false" maskchildren="true" keep="true">
		<layer name="fullscreen_btn" url="%SWFPATH%/skin/images/sprite.png" crop="0|0|85|85" onovercrop="0|85|85|85" />
	<layer name="link_2" type="container" bgcolor="0x91191b" bgalpha="1"  align="lefttop" width="85" height="85" x="0" y="0" scalechildren="false" maskchildren="true" keep="true" />
	<layer name="link_3" type="container" bgcolor="0xbb1c21" bgalpha="1"  align="lefttop" width="85" height="85" x="85" y="85" scalechildren="false" maskchildren="true" keep="true">
		<layer name="map_btn" url="%SWFPATH%/skin/images/sprite.png" crop="85|0|85|85" onovercrop="85|85|85|85" />
	<layer name="link_4" type="container" bgcolor="0xab1922" bgalpha="1"  align="lefttop" width="85" height="85" x="0" y="85" scalechildren="false" maskchildren="true" keep="true" />

Next to that, krpano now comes with it’s own editor! Simply create hotspots and set the initial view, then copy the generated xml file to your filesystem. This will definitely speed up the process of making tours. If your panorama’s contain GPS coordinates in the EXIF, then you’re in luck. Krpano can read those coordinates and generate a Bing Map with the hotspots already placed!



Area Margin

This script allows you to set the different margins of an area. It then scales the area to the remaining width and height.

	<events onresize="setMargin(10,10,10,10);" />
	<!-- setMargin(leftmargin,topmargin,bottommargin,rightmargin); -->
	<action name="setMargin">
		<!-- calculate remaining width. stagewidth - marginleft - marginright = area width -->
		sub(tmp1, stagewidth, %1); sub(tmp2, tmp1, %4); copy(area.width, tmp2);
		<!-- calculate remaining height. stageheight - margintop - marginbottom = area height -->
		sub(tmp3, stageheight, %2); sub(tmp4, tmp3, %3); copy(area.height, tmp4);
		<!--move the area-->
		set(area.x, %1); set(area.y, %2);

This script allows for a fixed width/height area in krpano. The area is then posistioned in the center of the screen.

<events onresize="define_area_size(1008,571);" />

	<action name="define_area_size">
		div(halfstagewidth, stagewidth, 2); div(halfareawidth, %1, 2); sub(dest_area_x, halfstagewidth, halfareawidth);
		copy(area.x, dest_area_x);
		div(halfstageheight, stageheight, 2); div(halfareaheight, %2, 2); sub(dest_area_y, halfstageheight, halfareaheight);
		copy(area.y, dest_area_y);
		set(area.width, %1);
		set(area.height, %2);

720 Degree panorama

Today Klaus (from showed a great example of a 720 degree panorama by blending two “hotspots” when a certain view.hlookat(pan) is reached.


Krpano using outside variables

You can define variables from outside krpano in your html and then send it to krpano. This has some advantages, you can for instance define a language or version and then send it to krpano.

var viewer = createPanoViewer({swf:"krpano.swf", xml:"tour.xml", target:"pano"});
viewer.addVariable("language", 'uk');
viewer.addVariable("version", '0.1');

In krpano you can get the variable by simply using get(language) and get(version).

There is one exception. If you wish to use it in a url sequence, you have to use the following format: %&VARIABLE%


<include url="file.xml?v=%$version%" />

The last example is a good way to prevent flash caching. When you update your krpano tour. Just change the version number in your html (or use php, and read the modified date). All the xml’s you include or plugin/hotspots urls will append this version number and is thus considered as a new file and downloaded again by the browser. This is a much cleaner way then my previous flash cachebusting solution.


XML Parsing error checklist for krpano.

When you get an error such as this one:

ERROR: XML parsing failed - ReferenceError: Error #1069

It means you have made a typo in your xml. Try to retrace your steps, what’s the last thing you’ve changed? I usually cut pieces from my code, save, and refresh the browser (caching refreshed/disabled). Still got the error? Then it’s almost certain, the code you just cut, is error free. Paste it back in or redo, and cut the next part etc. You could also comment out your code using <!-- --> But since I comment my code so much, this method doesn’t apply for me (See the part about commenting).

Once you find the part with the error in it, check for these issues:

Duplicate attributes

Do you have duplicate attributes? ex: <plugin name="test" url="image.png" keep="true" visible="true" keep="false" />

In this example you already have a keep attribute defined. Flash deals with this perfectly fine (last one overwrites the first). But Javascript (iPad) has stricter rules.

Alsoo you can’t have the same attribute name as your nodename. ex:

<plugin menu="true" name="test">
<menu name="testmenu" color="0xFFFFFF" />

In this example you have an attribute called menu. But later you define a sub node of the xml with the same name. plugin[test].menu references to true but alsoo to the sub node: menu[testmenu].

End with a semicolon

– Do you end every line with a semicolon ; in your actions? Careful with if statements, for loops and other commands that use a comma , to split a command in multiple parts. Place a semicolon at the end of the complete statement, after the “)”.

Close your xml tags

– Did you close every xml tag? This can be a “hard” close,  <plugin name="test"></plugin> or an inline close <plugin name="test" /> . Every xml tag needs to be closed, being an action, include or hotspot. There is no difference between a hard close or  a inline close. The only difference being, you can add code before you close a hard close. Most common seen in actions

<action name="foo">

Plugins don’t require code after their tag and thus can be closed inline.

This rule also applies to includes. The xml needs a start and end tag. With Krpano, 99% of the cases this is <krpano></krpano> . When writing custom xml that needs to be included, I sometimes forget this one.


– Be careful when commenting code. The correct way is <!– the message –> Don’t add three  dashes (-) or have comments in comments:

<!-- commented out for testing
<!-- a test plugin -->
<plugin name="test" />
 <!-- a plugin that controls fullscreen -->
<plugin name="fs" />

Also don’t add comments before the initial tag (the <krpano> ) or before the XML DOCTYPE <?xml version="1.0"?>

Reserved characters

– XML has several reserved characters you cannot use in your code ( You have to use their save alternative:

& - &amp;
< - &lt;
> -  &gt;
" - &quot;
' - &#39;

The & for instance is common when you add a url in the xml. Make sure it’s save encoded! Handy url that does it for you:

Seperate attributes

– Keep attributes seperated with a space. keep="true"visible="false"  results in an error. keep="true" visible="false" doesn’t.

Other tips

If you still have troubles to find/fix your error, try dragging your xml in your browser. IE for instance tells you at which line it fails. Furthermore, this is a great online validator:


Krpano Hotspot or Plugin Url not loading?

Today I tried to use a Facebook userpicture as a hotspot. It worked, but for some reason It didn’t accept the parameter ?type=square which provides a smaller squaresized picture.

But using the html code of & #63; (without the space between & and #) instead of ? I got it to work again.

Full url: #63;type=square