The export as SVG is indeed easy.
I've added this helper class to CardsMorphic-StephanEggermont.56
It is not well-factored, and for now ignores font families.

A class helper method to directly create a file:

CardSvgWriter class>newFile: aString on: aCardWall
        ^self new write: aCardWall to: (aString asFileReference writeStream )

Write a SVG header, the size and then the contents of the cardwall,
finally close the SVG.

CardSvgWriter>>write: aWall to: aStream
        stream := aStream.
        stream nextPutAll: self fileHeader.
        self fileSized: aWall fullFrame.
        stream cr.
        aWall cardSpace submorphsDo: [ :column |
                self writeColumn: column].
        stream nextPutAll: '</svg>'.
        stream close
        
SVG is an XML format

CardSvgWriter>>fileHeader
        ^'<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
'

Take the SVG size from the CardWall morph. Two namespaces are needed,
the second one to make the images work.

CardSvgWriter>>fileSized: frame
stream nextPutAll: '<svg xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; viewBox = "',
                (frame left) asString,' ',
                (frame top) asString,' ',
                (frame width) asString,' ',
                (frame height) asString,'" version = "1.1">'

For each column, write the title and a gray background

CardSvgWriter>>writeColumn: aColumn
        |background|
stream nextPutAll: '<text x="',(aColumn left+2) asString,'" y="',(aColumn top+2) asString,'" fill="gray" font-size= "11">'.
        stream nextPutAll: aColumn title.
        stream nextPutAll: '</text>'.
        stream cr.
        background := aColumn submorphs first.  
        stream nextPutAll:      '<rect x= "',(background left) asString,
                '" y="',(background top) asString,
                '" width="',(background width) asString,
                '" height="',(background height) asString,
                '" fill="rgb(250,250,250)" />'.
                stream cr.
        background submorphsDo: [ :card | self writeCard: card ].       

for each card, write its components

CardSvgWriter>>writeCard: aCard
        self writeCardRectangle: aCard.
        self writeCardText: aCard.
        self writeCardImage: aCard

The rectangle still has hardcoded border and borderColor

CardSvgWriter>>writeCardRectangle: aCard
        stream nextPutAll:      '<rect x= "',(aCard left) asString,
                '" y="',(aCard top) asString,
                '" width="',(aCard width) asString,
                '" height="',(aCard height) asString,
                '" stroke="','rgb(240,240,240)'"aCard borderColor asHTMLColor",
                '" stroke-width="', '1',"aCard borderWidth asString,"
                '" fill="',aCard color asHTMLColor,'" />'.
                stream cr.

Positioning of text is different in SVG and Morphic. In SVG,
it is the left-bottom, vs left-top in Morphic.

CardSVGWriter>>writeCardText: aCard
stream nextPutAll: '<text x="',(aCard left+2) asString,'" y="',(aCard top+12) asString,'" fill="black" font-size= "10">'.
        stream nextPutAll: aCard title.
        stream nextPutAll: '</text>'.
        stream cr.

The avatars are jpegs. they need to be base64 encoded.
Here they are repeated for each card, they should be
written once and linked.

CardSVGWriter>writeCardImage: aCard
        aCard person ifNotNil: [
stream nextPutAll: '<image x="',(aCard right-32) asString,'" y="',(aCard bottom-32) asString,'" width="32" height="32" xlink:href="data:image/jpg;base64,',
        (ZnUtils encodeBase64: (ByteArray streamContents: [:s |
PluginBasedJPEGReadWriter putForm: (AvatarCache default imageFor: aCard person) onStream: s]))
        ,'"/>'.
        stream cr]

Reply via email to