mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-12 21:47:00 -04:00
Implemented exporting symbol fonts;
Rendering symbols to an image always uses anti-aliassing (by downsampling from a large size); Finished the spoiler export template; Added <soft-line> tag to make line breaks use the line height for soft line breaks git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@440 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -409,6 +409,7 @@ label:
|
|||||||
button:
|
button:
|
||||||
# Style panel
|
# Style panel
|
||||||
use for all cards: Use for &all cards
|
use for all cards: Use for &all cards
|
||||||
|
use custom styling options: Options &specific to this card
|
||||||
|
|
||||||
# Keywords panel
|
# Keywords panel
|
||||||
insert parameter: Insert Parameter...
|
insert parameter: Insert Parameter...
|
||||||
@@ -476,7 +477,8 @@ title:
|
|||||||
# export
|
# export
|
||||||
export images: Export Images
|
export images: Export Images
|
||||||
export cancled: Export Cancled
|
export cancled: Export Cancled
|
||||||
export html: Export HTML
|
export html: Export to HTML
|
||||||
|
save html: Export to HTML
|
||||||
|
|
||||||
############################################################## Action (undo/redo) names
|
############################################################## Action (undo/redo) names
|
||||||
action:
|
action:
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 49 B |
@@ -18,10 +18,9 @@ option field:
|
|||||||
choice: no
|
choice: no
|
||||||
choice: just the image box, linked
|
choice: just the image box, linked
|
||||||
choice: just the image box, inline
|
choice: just the image box, inline
|
||||||
choice: full images, linked
|
choice: full card image, linked
|
||||||
choice: full images, preview
|
choice: full card image, preview
|
||||||
choice: full images, inline
|
choice: full card image only
|
||||||
choice: full images only
|
|
||||||
initial: full images, preview
|
initial: full images, preview
|
||||||
option field:
|
option field:
|
||||||
type: boolean
|
type: boolean
|
||||||
@@ -31,10 +30,11 @@ option field:
|
|||||||
type: boolean
|
type: boolean
|
||||||
name: rarity symbols
|
name: rarity symbols
|
||||||
description: Should rarity be shown using a symbol or as text?
|
description: Should rarity be shown using a symbol or as text?
|
||||||
option field:
|
#doesn't work yet:
|
||||||
type: boolean
|
#option field:
|
||||||
name: list keywords
|
# type: boolean
|
||||||
description: Should the keywords be listed?
|
# name: list keywords
|
||||||
|
# description: Should the keywords be listed?
|
||||||
#option field:
|
#option field:
|
||||||
# type: boolean
|
# type: boolean
|
||||||
# name: fancy scripts
|
# name: fancy scripts
|
||||||
@@ -83,28 +83,53 @@ option style:
|
|||||||
sans-serif: /magic-spoiler.mse-export-template/sans-serif.png
|
sans-serif: /magic-spoiler.mse-export-template/sans-serif.png
|
||||||
|
|
||||||
script:
|
script:
|
||||||
|
symbol_font := "magic-mana-small"
|
||||||
|
symbol_font_size := 12
|
||||||
write_card := {
|
write_card := {
|
||||||
if contains(options.images, match:= "full images") then
|
if contains(options.images, match:"full card image") then
|
||||||
card_image_file := write_image_file(card, file:"card{position(of:card,in:set)}.jpg")
|
card_image_file := write_image_file(card, file:"card{position(of:card,in:set)}.jpg")
|
||||||
else if contains(options.images, match:= "image box") then
|
else if contains(options.images, match:"image box") and
|
||||||
|
card.image != "" then
|
||||||
card_image_file := write_image_file(card.image, file:"card{position(of:card,in:set)}.jpg")
|
card_image_file := write_image_file(card.image, file:"card{position(of:card,in:set)}.jpg")
|
||||||
else
|
else
|
||||||
card_image_file := ""
|
card_image_file := ""
|
||||||
#if options.images == "full images only"
|
if options.images == "full card image, preview" then
|
||||||
"<li class='card'>
|
card_image_preview := write_image_file(card, file:"card-preview{position(of:card,in:set)}.jpg", height: 100)
|
||||||
<a href='{card_image_file}'><img src='{card_image_file}' alt='' class='image'></a>
|
else
|
||||||
<span class='name' >{ to_html(card.name ) }</span>
|
card_image_preview := card_image_file
|
||||||
<span class='casting-cost' >{ to_html(card.casting_cost, symbol_font: "mana-large", symbol_size: 12 ) }</span>
|
if options.images == "full card image only" then
|
||||||
|
"<li class='fullcard'><img src='{card_image_file}' alt=''></li>"
|
||||||
|
else
|
||||||
|
"<li class='card'>
|
||||||
|
{if options.images == "full card image, preview" then
|
||||||
|
"<a href='{card_image_file}'><img src='{card_image_preview}' alt='' class='card-image'></a>
|
||||||
|
<span class='name' >{ to_html(card.name ) }</span>"
|
||||||
|
else if card_image_file != "" and contains(options.images, match:"linked") then
|
||||||
|
"<span class='name' ><a href='{card_image_file}'>{ to_html(card.name) }</a></span>"
|
||||||
|
else
|
||||||
|
"<span class='name' >{ to_html(card.name ) }</span>"
|
||||||
|
}<span class='casting-cost' >{ symbols_to_html(card.casting_cost ) }</span>
|
||||||
|
{if card_image_file != "" and contains(options.images, match:"inline") then
|
||||||
|
"<img src='{card_image_preview}' alt='' class='image'>"
|
||||||
|
}
|
||||||
<span class='type' >{ to_html(card.type ) }</span>
|
<span class='type' >{ to_html(card.type ) }</span>
|
||||||
<span class='rarity' >{
|
<span class='rarity' >{
|
||||||
|
code := if card.rarity == "" then "C"
|
||||||
|
else if card.rarity == "basic land" then "L"
|
||||||
|
else to_upper(card.rarity[0]) # L,C,U,R,S
|
||||||
if options.rarity_symbols then
|
if options.rarity_symbols then
|
||||||
"<img src='{ write_image_file(
|
"<img src='{ var := if card.rarity == "" then "common"
|
||||||
name: "set-symbol-{card.rarity[0] or else "C"}.png",
|
else if card.rarity == "basic land" then "common"
|
||||||
|
else card.rarity
|
||||||
|
write_image_file(
|
||||||
|
file: "set-symbol-{var}.png",
|
||||||
width: 20,
|
width: 20,
|
||||||
image: symbol_variation(set.symbol, variation:card.rarity)
|
symbol_variation(
|
||||||
)}' alt='{card.rarity}'>"
|
symbol: set.symbol,
|
||||||
else
|
variation: var
|
||||||
to_upper(card.rarity[0] or else "C") # B,C,U,R,S
|
)
|
||||||
|
)}' alt='{code}' title='{card.rarity}'>"
|
||||||
|
else code
|
||||||
}</span>
|
}</span>
|
||||||
<span class='rule-text' >{ to_html(card.rule_text ) }</span>
|
<span class='rule-text' >{ to_html(card.rule_text ) }</span>
|
||||||
<span class='flavor-text' >{ to_html( remove_tag(tag: "<i-flavor>", card.flavor_text) ) }</span>
|
<span class='flavor-text' >{ to_html( remove_tag(tag: "<i-flavor>", card.flavor_text) ) }</span>
|
||||||
@@ -125,11 +150,13 @@ script:
|
|||||||
"<h2>{title} ({count} {if count == 1 then "card" else "cards"})</h2>" +
|
"<h2>{title} ({count} {if count == 1 then "card" else "cards"})</h2>" +
|
||||||
write_cards()
|
write_cards()
|
||||||
}
|
}
|
||||||
html := "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN\' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>
|
copy_file("blank.gif")
|
||||||
|
html := "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">
|
||||||
<html lang='en'>
|
<html lang='en'>
|
||||||
<head>
|
<head>
|
||||||
<title>{ to_html(set.title) }</title>
|
<title>{ to_html(set.title) }</title>
|
||||||
<link rel='stylesheet' type='text/css' href='{copy_file("style.css")}' />
|
<link rel='stylesheet' type='text/css' href='{copy_file("style.css")}' />
|
||||||
|
<script type='text/javascript' src='{copy_file("script.js")}'></script>
|
||||||
<style type='text/css'>
|
<style type='text/css'>
|
||||||
body \{
|
body \{
|
||||||
background: {options.background_color};
|
background: {options.background_color};
|
||||||
@@ -140,7 +167,12 @@ script:
|
|||||||
\}
|
\}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body{if options.images == "full card image, preview" then " class='with-previews'"}>
|
||||||
|
<img src='{ write_image_file(
|
||||||
|
file: "set-symbol.png",
|
||||||
|
width: 200,
|
||||||
|
symbol_variation(symbol: set.symbol, variation: "rare")
|
||||||
|
)}' alt='' class='set-symbol'>
|
||||||
<h1>{ to_html(set.title) }</h1>
|
<h1>{ to_html(set.title) }</h1>
|
||||||
<div class='copyright'>{ to_html(set.copyright) }</div>
|
<div class='copyright'>{ to_html(set.copyright) }</div>
|
||||||
<div class='description'>{ to_html(set.description) }</div>
|
<div class='description'>{ to_html(set.description) }</div>
|
||||||
@@ -159,6 +191,9 @@ script:
|
|||||||
else
|
else
|
||||||
write_cards(cards: set.cards)
|
write_cards(cards: set.cards)
|
||||||
}
|
}
|
||||||
|
<script><!--
|
||||||
|
init();
|
||||||
|
--></script>
|
||||||
</body>
|
</body>
|
||||||
</html>"
|
</html>"
|
||||||
write_text_file(html, file:"index.html")
|
write_text_file(html, file:"index.html")
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
var isIE = navigator.appVersion.indexOf("MSIE") != -1;
|
||||||
|
|
||||||
|
var preview, preview_img;
|
||||||
|
|
||||||
|
function show_preview(url) {
|
||||||
|
preview.style.display = "block";
|
||||||
|
preview_img.style.backgroundImage = "url("+this.href+")";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide_preview() {
|
||||||
|
preview.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
function fix_preview() {
|
||||||
|
var e = document.documentElement ? document.documentElement : document.body;
|
||||||
|
preview.style.top = e.scrollTop + "px";
|
||||||
|
preview.style.height = e.clientHeight;
|
||||||
|
preview.style.width = e.clientWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nice_preview() {
|
||||||
|
// attach
|
||||||
|
var links = document.getElementsByTagName("A");
|
||||||
|
for (var i in links) {
|
||||||
|
if (/(.jpg|.png|.gif)$/.test(links[i])) {
|
||||||
|
links[i].onclick = show_preview;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create divs
|
||||||
|
preview = document.createElement("div");
|
||||||
|
var bg = document.createElement("div");
|
||||||
|
var img = document.createElement("div");
|
||||||
|
preview.id = "preview";
|
||||||
|
bg.id = "preview-bg";
|
||||||
|
img.id = "preview-img";
|
||||||
|
hide_preview();
|
||||||
|
preview.onclick = bg.onclick = img.onclick = hide_preview;
|
||||||
|
preview.appendChild(bg);
|
||||||
|
preview.appendChild(img);
|
||||||
|
document.body.appendChild(preview);
|
||||||
|
preview_img = img;
|
||||||
|
if (isIE) {
|
||||||
|
window.onscroll = fix_preview;
|
||||||
|
fix_preview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fix_png_alpha() {
|
||||||
|
if (!/MSIE (5\.5|6\.)/.test(navigator.userAgent)) return; // only in ie 5.5 and 6
|
||||||
|
var dir = document.getElementsByTagName("SCRIPT")[0].src.replace(/[^\/]*$/,''); // dir for blank image
|
||||||
|
var imgs = document.getElementsByTagName("IMG");
|
||||||
|
for (var i in imgs) {
|
||||||
|
var img = imgs[i];
|
||||||
|
if ((/\.png$/i).test(img.src)) {
|
||||||
|
if (img.currentStyle.width == 'auto' && img.currentStyle.height == 'auto') {
|
||||||
|
img.style.width = img.offsetWidth + 'px';
|
||||||
|
img.style.height = img.offsetHeight + 'px';
|
||||||
|
}
|
||||||
|
img.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+img.src+'",sizingMethod="scale")';
|
||||||
|
img.src = dir + "blank.gif";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
fix_png_alpha();
|
||||||
|
nice_preview();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
|
.set-symbol {
|
||||||
|
float: left;
|
||||||
|
margin-right: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
.card {
|
.card {
|
||||||
|
clear: left;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,6 +27,7 @@ ul {
|
|||||||
display: inline;
|
display: inline;
|
||||||
font-family: "Magic Symbols", "Magic Symbols 2004";
|
font-family: "Magic Symbols", "Magic Symbols 2004";
|
||||||
font-size: larger;
|
font-size: larger;
|
||||||
|
margin-left: .1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card .flavor-text {
|
.card .flavor-text {
|
||||||
@@ -32,16 +39,62 @@ ul {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.card {
|
.with-previews .card {
|
||||||
margin-top: 1.1em;
|
margin-top: 1.1em;
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
margin-left: 90px;
|
margin-left: 90px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.card .image {
|
.card .card-image {
|
||||||
height: 100px;
|
height: 100px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -85px;
|
left: -85px;
|
||||||
top: 3px;
|
top: 3px;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
.card .image {
|
||||||
|
display: block;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.symbol {
|
||||||
|
display: inline;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullcard {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* image preview */
|
||||||
|
#preview-bg {
|
||||||
|
background-color: rgb(0,0,0);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0.7;
|
||||||
|
-moz-opacity: 0.7;
|
||||||
|
filter: alpha(opacity=70);
|
||||||
|
}
|
||||||
|
#preview-img {
|
||||||
|
background-position: 50% 50%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
#preview {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
* html #preview {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
@@ -48,6 +48,7 @@ struct ExportInfo {
|
|||||||
String directory_relative; ///< The directory for storing extra files (or "" if !export->create_directory)
|
String directory_relative; ///< The directory for storing extra files (or "" if !export->create_directory)
|
||||||
/// This is just the directory name
|
/// This is just the directory name
|
||||||
String directory_absolute; ///< The absolute path of the directory
|
String directory_absolute; ///< The absolute path of the directory
|
||||||
|
set<String> exported_images; ///< Images (from symbol font) already exported
|
||||||
};
|
};
|
||||||
|
|
||||||
DECLARE_DYNAMIC_ARG(ExportInfo*, export_info);
|
DECLARE_DYNAMIC_ARG(ExportInfo*, export_info);
|
||||||
|
|||||||
+66
-23
@@ -74,6 +74,9 @@ class SymbolInFont : public IntrusivePtrBase<SymbolInFont> {
|
|||||||
public:
|
public:
|
||||||
SymbolInFont();
|
SymbolInFont();
|
||||||
|
|
||||||
|
/// Get a shrunk, zoomed image
|
||||||
|
Image getImage(Package& pkg, double size);
|
||||||
|
|
||||||
/// Get a shrunk, zoomed bitmap
|
/// Get a shrunk, zoomed bitmap
|
||||||
Bitmap getBitmap(Package& pkg, double size);
|
Bitmap getBitmap(Package& pkg, double size);
|
||||||
|
|
||||||
@@ -107,23 +110,26 @@ SymbolInFont::SymbolInFont()
|
|||||||
if (img_size <= 0) img_size = 1;
|
if (img_size <= 0) img_size = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Image SymbolInFont::getImage(Package& pkg, double size) {
|
||||||
|
// generate new image
|
||||||
|
if (!image.isReady()) {
|
||||||
|
throw Error(_("No image specified for symbol with code '") + code + _("' in symbol font."));
|
||||||
|
}
|
||||||
|
Image img = image.generate(GeneratedImage::Options(0, 0, &pkg));
|
||||||
|
actual_size = wxSize(img.GetWidth(), img.GetHeight());
|
||||||
|
// scale to match expected size
|
||||||
|
Image resampled_image((int) (actual_size.GetWidth() * size / img_size),
|
||||||
|
(int) (actual_size.GetHeight() * size / img_size), false);
|
||||||
|
if (!resampled_image.Ok()) return Image(1,1);
|
||||||
|
resample(img, resampled_image);
|
||||||
|
return resampled_image;
|
||||||
|
}
|
||||||
Bitmap SymbolInFont::getBitmap(Package& pkg, double size) {
|
Bitmap SymbolInFont::getBitmap(Package& pkg, double size) {
|
||||||
// is this bitmap already loaded/generated?
|
// is this bitmap already loaded/generated?
|
||||||
Bitmap& bmp = bitmaps[size];
|
Bitmap& bmp = bitmaps[size];
|
||||||
if (!bmp.Ok()) {
|
if (!bmp.Ok()) {
|
||||||
// generate new bitmap
|
// generate image, convert to bitmap, store for later use
|
||||||
if (!image.isReady()) {
|
bmp = Bitmap(getImage(pkg, size));
|
||||||
throw Error(_("No image specified for symbol with code '") + code + _("' in symbol font."));
|
|
||||||
}
|
|
||||||
Image img = image.generate(GeneratedImage::Options(0, 0, &pkg));
|
|
||||||
actual_size = wxSize(img.GetWidth(), img.GetHeight());
|
|
||||||
// scale to match expected size
|
|
||||||
Image resampled_image((int) (actual_size.GetWidth() * size / img_size),
|
|
||||||
(int) (actual_size.GetHeight() * size / img_size), false);
|
|
||||||
if (!resampled_image.Ok()) return Bitmap(1,1);
|
|
||||||
resample(img, resampled_image);
|
|
||||||
// convert to bitmap, store for later use
|
|
||||||
bmp = Bitmap(resampled_image);
|
|
||||||
}
|
}
|
||||||
return bmp;
|
return bmp;
|
||||||
}
|
}
|
||||||
@@ -166,16 +172,6 @@ IMPLEMENT_REFLECTION(SymbolInFont) {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : SymbolFont : splitting
|
// ----------------------------------------------------------------------------- : SymbolFont : splitting
|
||||||
|
|
||||||
class SymbolFont::DrawableSymbol {
|
|
||||||
public:
|
|
||||||
DrawableSymbol(const String& text, SymbolInFont* symbol)
|
|
||||||
: text(text), symbol(symbol)
|
|
||||||
{}
|
|
||||||
|
|
||||||
String text; ///< Original text
|
|
||||||
SymbolInFont* symbol; ///< Symbol to draw, if nullptr, use the default symbol and draw the text
|
|
||||||
};
|
|
||||||
|
|
||||||
void SymbolFont::split(const String& text, SplitSymbols& out) const {
|
void SymbolFont::split(const String& text, SplitSymbols& out) const {
|
||||||
// read a single symbol until we are done with the text
|
// read a single symbol until we are done with the text
|
||||||
for (size_t pos = 0 ; pos < text.size() ; ) {
|
for (size_t pos = 0 ; pos < text.size() ; ) {
|
||||||
@@ -286,6 +282,53 @@ void SymbolFont::drawWithText(RotatedDC& dc, const RealRect& rect, double font_s
|
|||||||
dc.DrawText(text, text_pos);
|
dc.DrawText(text, text_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Image SymbolFont::getImage(double font_size, const DrawableSymbol& sym) {
|
||||||
|
if (sym.symbol) {
|
||||||
|
return sym.symbol->getImage(*this, font_size);
|
||||||
|
} else {
|
||||||
|
if (!text_font) return Image(1,1); // failed
|
||||||
|
// draw on default symbol
|
||||||
|
SymbolInFont* def = defaultSymbol();
|
||||||
|
if (!def) return Image(1,1); // failed
|
||||||
|
Bitmap bmp(def->getImage(*this, font_size));
|
||||||
|
// memory dc to work with
|
||||||
|
wxMemoryDC dc;
|
||||||
|
dc.SelectObject(bmp);
|
||||||
|
RealRect sym_rect(0,0,bmp.GetWidth(),bmp.GetHeight());
|
||||||
|
RotatedDC rdc(dc, 0, sym_rect, 1, QUALITY_AA);
|
||||||
|
// subtract margins from size
|
||||||
|
sym_rect.x += text_margin_left;
|
||||||
|
sym_rect.y += text_margin_top;
|
||||||
|
sym_rect.width -= text_margin_left + text_margin_right;
|
||||||
|
sym_rect.height -= text_margin_top + text_margin_bottom;
|
||||||
|
// setup text, shrink it
|
||||||
|
double size = text_font->size; // TODO : incorporate shrink factor?
|
||||||
|
RealSize ts;
|
||||||
|
while (true) {
|
||||||
|
if (size <= 0) return def->getImage(*this, font_size); // text too small
|
||||||
|
rdc.SetFont(*text_font, size / text_font->size);
|
||||||
|
ts = rdc.GetTextExtent(sym.text);
|
||||||
|
if (ts.width <= sym_rect.width && ts.height <= sym_rect.height) {
|
||||||
|
break; // text fits
|
||||||
|
} else {
|
||||||
|
// text doesn't fit
|
||||||
|
size -= rdc.getFontSizeStep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// align text
|
||||||
|
RealPoint text_pos = align_in_rect(text_alignment, ts, sym_rect);
|
||||||
|
// draw text
|
||||||
|
if (text_font->hasShadow()) {
|
||||||
|
rdc.SetTextForeground(text_font->shadow_color);
|
||||||
|
rdc.DrawText(sym.text, text_pos + text_font->shadow_displacement);
|
||||||
|
}
|
||||||
|
rdc.SetTextForeground(text_font->color);
|
||||||
|
rdc.DrawText(sym.text, text_pos);
|
||||||
|
// done
|
||||||
|
dc.SelectObject(wxNullBitmap);
|
||||||
|
return bmp.ConvertToImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : SymbolFont : sizes
|
// ----------------------------------------------------------------------------- : SymbolFont : sizes
|
||||||
|
|
||||||
|
|||||||
@@ -35,12 +35,22 @@ class SymbolFont : public Packaged {
|
|||||||
// Script update
|
// Script update
|
||||||
void update(Context& ctx) const;
|
void update(Context& ctx) const;
|
||||||
|
|
||||||
class DrawableSymbol;
|
/// A symbol to be drawn
|
||||||
|
class DrawableSymbol {
|
||||||
|
public:
|
||||||
|
inline DrawableSymbol(const String& text, SymbolInFont* symbol)
|
||||||
|
: text(text), symbol(symbol)
|
||||||
|
{}
|
||||||
|
|
||||||
|
String text; ///< Original text
|
||||||
|
SymbolInFont* symbol; ///< Symbol to draw, if nullptr, use the default symbol and draw the text
|
||||||
|
};
|
||||||
typedef vector<DrawableSymbol> SplitSymbols;
|
typedef vector<DrawableSymbol> SplitSymbols;
|
||||||
|
|
||||||
/// Split a string into separate symbols for drawing and for determining their size
|
/// Split a string into separate symbols for drawing and for determining their size
|
||||||
void split(const String& text, SplitSymbols& out) const;
|
void split(const String& text, SplitSymbols& out) const;
|
||||||
|
|
||||||
/// Draw a piece of text prepared using split
|
/// Draw a piece of text
|
||||||
void draw(RotatedDC& dc, Context& ctx, const RealRect& rect, double font_size, const Alignment& align, const String& text);
|
void draw(RotatedDC& dc, Context& ctx, const RealRect& rect, double font_size, const Alignment& align, const String& text);
|
||||||
/// Get information on characters in a string
|
/// Get information on characters in a string
|
||||||
void getCharInfo(RotatedDC& dc, Context& ctx, double font_size, const String& text, vector<CharInfo>& out);
|
void getCharInfo(RotatedDC& dc, Context& ctx, double font_size, const String& text, vector<CharInfo>& out);
|
||||||
@@ -50,6 +60,9 @@ class SymbolFont : public Packaged {
|
|||||||
/// Get information on characters in a string
|
/// Get information on characters in a string
|
||||||
void getCharInfo(RotatedDC& dc, double font_size, const SplitSymbols& text, vector<CharInfo>& out);
|
void getCharInfo(RotatedDC& dc, double font_size, const SplitSymbols& text, vector<CharInfo>& out);
|
||||||
|
|
||||||
|
/// Get the image for a symbol
|
||||||
|
Image getImage(double font_size, const DrawableSymbol& symbol);
|
||||||
|
|
||||||
static String typeNameStatic();
|
static String typeNameStatic();
|
||||||
virtual String typeName() const;
|
virtual String typeName() const;
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,58 @@
|
|||||||
ScriptType GeneratedImage::type() const { return SCRIPT_IMAGE; }
|
ScriptType GeneratedImage::type() const { return SCRIPT_IMAGE; }
|
||||||
String GeneratedImage::typeName() const { return _TYPE_("image"); }
|
String GeneratedImage::typeName() const { return _TYPE_("image"); }
|
||||||
|
|
||||||
|
Image GeneratedImage::generateConform(const Options& options) const {
|
||||||
|
return conform_image(generate(options),options);
|
||||||
|
}
|
||||||
|
|
||||||
|
Image conform_image(const Image& img, const GeneratedImage::Options& options) {
|
||||||
|
Image image = img;
|
||||||
|
// resize?
|
||||||
|
int iw = image.GetWidth(), ih = image.GetHeight();
|
||||||
|
if ((iw == options.width && ih == options.height) || (options.width == 0 && options.height == 0)) {
|
||||||
|
// already the right size
|
||||||
|
} else if (options.height == 0) {
|
||||||
|
// width is given, determine height
|
||||||
|
int h = options.width * ih / iw;
|
||||||
|
Image resampled_image(options.width, h, false);
|
||||||
|
resample(image, resampled_image);
|
||||||
|
image = resampled_image;
|
||||||
|
} else if (options.width == 0) {
|
||||||
|
// height is given, determine width
|
||||||
|
int w = options.height * iw / ih;
|
||||||
|
Image resampled_image(w, options.height, false);
|
||||||
|
resample(image, resampled_image);
|
||||||
|
image = resampled_image;
|
||||||
|
} else if (options.preserve_aspect == ASPECT_FIT) {
|
||||||
|
// determine actual size of resulting image
|
||||||
|
int w, h;
|
||||||
|
if (iw * options.height > ih * options.width) { // too much height requested
|
||||||
|
w = options.width;
|
||||||
|
h = options.width * ih / iw;
|
||||||
|
} else {
|
||||||
|
w = options.height * iw / ih;
|
||||||
|
h = options.height;
|
||||||
|
}
|
||||||
|
Image resampled_image(w, h, false);
|
||||||
|
resample(image, resampled_image);
|
||||||
|
image = resampled_image;
|
||||||
|
} else {
|
||||||
|
Image resampled_image(options.width, options.height, false);
|
||||||
|
if (options.preserve_aspect == ASPECT_BORDER && (options.width < options.height * 3) && (options.height < options.width * 3)) {
|
||||||
|
// preserve the aspect ratio if there is not too much difference
|
||||||
|
resample_preserve_aspect(image, resampled_image);
|
||||||
|
} else {
|
||||||
|
resample(image, resampled_image);
|
||||||
|
}
|
||||||
|
image = resampled_image;
|
||||||
|
}
|
||||||
|
// saturate?
|
||||||
|
if (options.saturate) {
|
||||||
|
saturate(image, 40);
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : BlankImage
|
// ----------------------------------------------------------------------------- : BlankImage
|
||||||
|
|
||||||
Image BlankImage::generate(const Options& opt) const {
|
Image BlankImage::generate(const Options& opt) const {
|
||||||
@@ -165,7 +217,7 @@ Image SymbolToImage::generate(const Options& opt) const {
|
|||||||
} else {
|
} else {
|
||||||
the_symbol = opt.local_package->readFile<SymbolP>(filename);
|
the_symbol = opt.local_package->readFile<SymbolP>(filename);
|
||||||
}
|
}
|
||||||
return render_symbol(the_symbol, *variation->filter, variation->border_radius);
|
return render_symbol(the_symbol, *variation->filter, variation->border_radius, max(100, 3*max(opt.width,opt.height)));
|
||||||
}
|
}
|
||||||
bool SymbolToImage::operator == (const GeneratedImage& that) const {
|
bool SymbolToImage::operator == (const GeneratedImage& that) const {
|
||||||
const SymbolToImage* that2 = dynamic_cast<const SymbolToImage*>(&that);
|
const SymbolToImage* that2 = dynamic_cast<const SymbolToImage*>(&that);
|
||||||
@@ -190,7 +242,7 @@ Image ImageValueToImage::generate(const Options& opt) const {
|
|||||||
image.LoadFile(*image_file);
|
image.LoadFile(*image_file);
|
||||||
}
|
}
|
||||||
if (!image.Ok()) {
|
if (!image.Ok()) {
|
||||||
image = Image(opt.width, opt.height);
|
image = Image(max(1,opt.width), max(1,opt.height));
|
||||||
}
|
}
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ class GeneratedImage : public ScriptValue {
|
|||||||
Package* local_package; ///< Package to load symbols and ImageValue images from
|
Package* local_package; ///< Package to load symbols and ImageValue images from
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Generate the image, and conform to the options
|
||||||
|
Image generateConform(const Options&) const;
|
||||||
/// Generate the image
|
/// Generate the image
|
||||||
virtual Image generate(const Options&) const = 0;
|
virtual Image generate(const Options&) const = 0;
|
||||||
/// How must the image be combined with the background?
|
/// How must the image be combined with the background?
|
||||||
@@ -53,6 +55,9 @@ class GeneratedImage : public ScriptValue {
|
|||||||
virtual String typeName() const;
|
virtual String typeName() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Resize an image to conform to the options
|
||||||
|
Image conform_image(const Image&, const GeneratedImage::Options&);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : BlankImage
|
// ----------------------------------------------------------------------------- : BlankImage
|
||||||
|
|
||||||
/// An image generator that returns a blank image
|
/// An image generator that returns a blank image
|
||||||
|
|||||||
@@ -56,10 +56,6 @@ ImagesExportWindow::ImagesExportWindow(Window* parent, const SetP& set)
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Exporting the images
|
// ----------------------------------------------------------------------------- : Exporting the images
|
||||||
|
|
||||||
bool is_filename_char(Char c) {
|
|
||||||
return isAlnum(c) || c == _(' ') || c == _('_') || c == _('-') || c == _('.');
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImagesExportWindow::onOk(wxCommandEvent&) {
|
void ImagesExportWindow::onOk(wxCommandEvent&) {
|
||||||
// Update settings
|
// Update settings
|
||||||
GameSettings& gs = settings.gameSettingsFor(*set->game);
|
GameSettings& gs = settings.gameSettingsFor(*set->game);
|
||||||
@@ -85,16 +81,7 @@ void ImagesExportWindow::onOk(wxCommandEvent&) {
|
|||||||
String filename = untag(ctx.eval(*filename_script)->toString());
|
String filename = untag(ctx.eval(*filename_script)->toString());
|
||||||
if (!filename) continue; // no filename -> no saving
|
if (!filename) continue; // no filename -> no saving
|
||||||
// sanitize filename
|
// sanitize filename
|
||||||
String clean_filename;
|
fn.SetFullName(clean_filename(filename));
|
||||||
FOR_EACH(c, filename) {
|
|
||||||
if (is_filename_char(c)) {
|
|
||||||
clean_filename += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (clean_filename.empty() || starts_with(clean_filename, _("."))) {
|
|
||||||
clean_filename = _("no-name") + clean_filename;
|
|
||||||
}
|
|
||||||
fn.SetFullName(clean_filename);
|
|
||||||
// does the file exist?
|
// does the file exist?
|
||||||
if (fn.FileExists()) {
|
if (fn.FileExists()) {
|
||||||
// file exists, what to do?
|
// file exists, what to do?
|
||||||
|
|||||||
@@ -72,13 +72,13 @@ const size_t param_colors_count = sizeof(param_colors) / sizeof(param_colors[0])
|
|||||||
struct TextElementsFromString {
|
struct TextElementsFromString {
|
||||||
// What formatting is enabled?
|
// What formatting is enabled?
|
||||||
int bold, italic, symbol;
|
int bold, italic, symbol;
|
||||||
int soft, kwpph, param, line;
|
int soft, kwpph, param, line, soft_line;
|
||||||
int code, code_kw, code_string, param_ref, error;
|
int code, code_kw, code_string, param_ref, error;
|
||||||
int param_id;
|
int param_id;
|
||||||
bool bracket;
|
bool bracket;
|
||||||
|
|
||||||
TextElementsFromString()
|
TextElementsFromString()
|
||||||
: bold(0), italic(0), symbol(0), soft(0), kwpph(0), param(0), line(0)
|
: bold(0), italic(0), symbol(0), soft(0), kwpph(0), param(0), line(0), soft_line(0)
|
||||||
, code(0), code_kw(0), code_string(0), param_ref(0), error(0)
|
, code(0), code_kw(0), code_string(0), param_ref(0), error(0)
|
||||||
, param_id(0), bracket(false) {}
|
, param_id(0), bracket(false) {}
|
||||||
|
|
||||||
@@ -125,6 +125,8 @@ struct TextElementsFromString {
|
|||||||
else if (is_substr(text, tag_start, _("</atom-param"))) param -= 1;
|
else if (is_substr(text, tag_start, _("</atom-param"))) param -= 1;
|
||||||
else if (is_substr(text, tag_start, _( "<line"))) line += 1;
|
else if (is_substr(text, tag_start, _( "<line"))) line += 1;
|
||||||
else if (is_substr(text, tag_start, _("</line"))) line -= 1;
|
else if (is_substr(text, tag_start, _("</line"))) line -= 1;
|
||||||
|
else if (is_substr(text, tag_start, _( "<soft-line"))) soft_line += 1;
|
||||||
|
else if (is_substr(text, tag_start, _("</soft-line"))) soft_line -= 1;
|
||||||
else if (is_substr(text, tag_start, _("<atom"))) {
|
else if (is_substr(text, tag_start, _("<atom"))) {
|
||||||
// 'atomic' indicator
|
// 'atomic' indicator
|
||||||
size_t end_tag = min(end, match_close_tag(text, tag_start));
|
size_t end_tag = min(end, match_close_tag(text, tag_start));
|
||||||
@@ -178,7 +180,8 @@ struct TextElementsFromString {
|
|||||||
bracket ? pos + 2 : pos + 1,
|
bracket ? pos + 2 : pos + 1,
|
||||||
font,
|
font,
|
||||||
soft > 0 ? DRAW_ACTIVE : DRAW_NORMAL,
|
soft > 0 ? DRAW_ACTIVE : DRAW_NORMAL,
|
||||||
line > 0 ? BREAK_LINE : BREAK_HARD);
|
line > 0 ? BREAK_LINE :
|
||||||
|
soft_line > 0 ? BREAK_SOFT : BREAK_HARD);
|
||||||
}
|
}
|
||||||
if (bracket) {
|
if (bracket) {
|
||||||
e->content = String(LEFT_ANGLE_BRACKET) + c + RIGHT_ANGLE_BRACKET;
|
e->content = String(LEFT_ANGLE_BRACKET) + c + RIGHT_ANGLE_BRACKET;
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ enum DrawWhat
|
|||||||
enum LineBreak
|
enum LineBreak
|
||||||
{ BREAK_NO // no line break ever
|
{ BREAK_NO // no line break ever
|
||||||
, BREAK_MAYBE // break here when in "direction:vertical" mode
|
, BREAK_MAYBE // break here when in "direction:vertical" mode
|
||||||
, BREAK_SOFT // optional line break (' ')
|
, BREAK_SPACE // optional line break (' ')
|
||||||
|
, BREAK_SOFT // always a line break, spacing as a soft break
|
||||||
, BREAK_HARD // always a line break ('\n')
|
, BREAK_HARD // always a line break ('\n')
|
||||||
, BREAK_LINE // line break with a separator line (<line>)
|
, BREAK_LINE // line break with a separator line (<line>)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ void FontTextElement::getCharInfo(RotatedDC& dc, double scale, vector<CharInfo>&
|
|||||||
} else {
|
} else {
|
||||||
RealSize s = dc.GetTextExtent(content.substr(line_start - this->start, i - line_start + 1));
|
RealSize s = dc.GetTextExtent(content.substr(line_start - this->start, i - line_start + 1));
|
||||||
out.push_back(CharInfo(RealSize(s.width - prev_width, s.height),
|
out.push_back(CharInfo(RealSize(s.width - prev_width, s.height),
|
||||||
c == _(' ') ? BREAK_SOFT : BREAK_MAYBE
|
c == _(' ') ? BREAK_SPACE : BREAK_MAYBE
|
||||||
));
|
));
|
||||||
prev_width = s.width;
|
prev_width = s.width;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -419,7 +419,11 @@ bool TextViewer::prepareLinesScale(RotatedDC& dc, const vector<CharInfo>& chars,
|
|||||||
bool accept_word = false; // the current word should be added to the line
|
bool accept_word = false; // the current word should be added to the line
|
||||||
bool hide_breaker = true; // hide the \n or _(' ') that caused a line break
|
bool hide_breaker = true; // hide the \n or _(' ') that caused a line break
|
||||||
double line_height_multiplier = 1; // multiplier for line height for next line top
|
double line_height_multiplier = 1; // multiplier for line height for next line top
|
||||||
if (c.break_after == BREAK_HARD) {
|
if (c.break_after == BREAK_SOFT) {
|
||||||
|
break_now = true;
|
||||||
|
accept_word = true;
|
||||||
|
line_height_multiplier = style.line_height_soft;
|
||||||
|
} else if (c.break_after == BREAK_HARD) {
|
||||||
break_now = true;
|
break_now = true;
|
||||||
accept_word = true;
|
accept_word = true;
|
||||||
line_height_multiplier = style.line_height_hard;
|
line_height_multiplier = style.line_height_hard;
|
||||||
@@ -428,7 +432,7 @@ bool TextViewer::prepareLinesScale(RotatedDC& dc, const vector<CharInfo>& chars,
|
|||||||
break_now = true;
|
break_now = true;
|
||||||
accept_word = true;
|
accept_word = true;
|
||||||
line_height_multiplier = style.line_height_line;
|
line_height_multiplier = style.line_height_line;
|
||||||
} else if (c.break_after == BREAK_SOFT && style.field().multi_line) {
|
} else if (c.break_after == BREAK_SPACE && style.field().multi_line) {
|
||||||
// Soft break == end of word
|
// Soft break == end of word
|
||||||
accept_word = true;
|
accept_word = true;
|
||||||
} else if (c.break_after == BREAK_MAYBE && style.direction == TOP_TO_BOTTOM) {
|
} else if (c.break_after == BREAK_MAYBE && style.direction == TOP_TO_BOTTOM) {
|
||||||
@@ -563,13 +567,13 @@ void TextViewer::alignLines(RotatedDC& dc, const vector<CharInfo>& chars, const
|
|||||||
double hdelta = s.width - width; // amount of space to distribute
|
double hdelta = s.width - width; // amount of space to distribute
|
||||||
int count = 0; // distribute it among this many words
|
int count = 0; // distribute it among this many words
|
||||||
for (size_t k = l.start + 1 ; k < l.end() - 1 ; ++k) {
|
for (size_t k = l.start + 1 ; k < l.end() - 1 ; ++k) {
|
||||||
if (chars[k].break_after == BREAK_SOFT) ++count;
|
if (chars[k].break_after == BREAK_SPACE) ++count;
|
||||||
}
|
}
|
||||||
if (count == 0) count = 1; // prevent div by 0
|
if (count == 0) count = 1; // prevent div by 0
|
||||||
int i = 0; size_t j = l.start;
|
int i = 0; size_t j = l.start;
|
||||||
FOR_EACH(c, l.positions) {
|
FOR_EACH(c, l.positions) {
|
||||||
c += hdelta * i / count;
|
c += hdelta * i / count;
|
||||||
if (j < l.end() && chars[j++].break_after == BREAK_SOFT) i++;
|
if (j < l.end() && chars[j++].break_after == BREAK_SPACE) i++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// simple alignment
|
// simple alignment
|
||||||
|
|||||||
+162
-26
@@ -20,6 +20,19 @@
|
|||||||
#include <wx/wfstream.h>
|
#include <wx/wfstream.h>
|
||||||
#include <wx/filename.h>
|
#include <wx/filename.h>
|
||||||
|
|
||||||
|
DECLARE_TYPEOF_COLLECTION(SymbolFont::DrawableSymbol);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Utility
|
||||||
|
|
||||||
|
// Make sure we can export files to a data directory
|
||||||
|
void guard_export_info(const String& fun) {
|
||||||
|
if (!export_info()) {
|
||||||
|
throw ScriptError(_("Can only use ") + fun + _(" from export templates"));
|
||||||
|
} else if (export_info()->directory_relative.empty()) {
|
||||||
|
throw ScriptError(_("Can only use ") + fun + _(" when 'create directory' is set to true"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : HTML
|
// ----------------------------------------------------------------------------- : HTML
|
||||||
|
|
||||||
// An HTML tag
|
// An HTML tag
|
||||||
@@ -124,11 +137,52 @@ class TagStack {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
String symbols_to_html(const String& str, const SymbolFontP& symbol_font) {
|
// html-escape a string
|
||||||
return str; // TODO
|
String html_escape(const String& str) {
|
||||||
|
String ret;
|
||||||
|
FOR_EACH_CONST(c, str) {
|
||||||
|
if (c == _('\1') || c == _('<')) { // escape <
|
||||||
|
ret += _("<");
|
||||||
|
} else if (c == _('>')) { // escape >
|
||||||
|
ret += _(">");
|
||||||
|
} else if (c == _('&')) { // escape &
|
||||||
|
ret += _("&");
|
||||||
|
} else if (c == _('\'')) { // escape '
|
||||||
|
ret += _("'");
|
||||||
|
} else if (c == _('\"')) { // escape "
|
||||||
|
ret += _(""");
|
||||||
|
} else if (c >= 0x80) { // escape non ascii
|
||||||
|
ret += String(_("&#")) << (int)c << _(';');
|
||||||
|
} else {
|
||||||
|
ret += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write symbols to html
|
||||||
|
String symbols_to_html(const String& str, SymbolFont& symbol_font, double size) {
|
||||||
|
guard_export_info(_("symbols_to_html"));
|
||||||
|
ExportInfo& ei = *export_info();
|
||||||
|
vector<SymbolFont::DrawableSymbol> symbols;
|
||||||
|
symbol_font.split(str, symbols);
|
||||||
|
String html;
|
||||||
|
FOR_EACH(sym, symbols) {
|
||||||
|
String filename = symbol_font.name() + _("-") + clean_filename(sym.text) + _(".png");
|
||||||
|
html += _("<img src='") + filename + _("' alt='") + html_escape(sym.text) + _("'>");
|
||||||
|
if (ei.exported_images.insert(filename).second) {
|
||||||
|
// save symbol image
|
||||||
|
Image img = symbol_font.getImage(size, sym);
|
||||||
|
wxFileName fn;
|
||||||
|
fn.SetPath(ei.directory_absolute);
|
||||||
|
fn.SetFullName(filename);
|
||||||
|
img.SaveFile(fn.GetFullPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
String to_html(const String& str_in, const SymbolFontP& symbol_font) {
|
String to_html(const String& str_in, const SymbolFontP& symbol_font, double symbol_size) {
|
||||||
String str = remove_tag_contents(str_in,_("<sep-soft"));
|
String str = remove_tag_contents(str_in,_("<sep-soft"));
|
||||||
String ret;
|
String ret;
|
||||||
Tag bold (_("<b>"), _("</b>")),
|
Tag bold (_("<b>"), _("</b>")),
|
||||||
@@ -149,12 +203,12 @@ String to_html(const String& str_in, const SymbolFontP& symbol_font) {
|
|||||||
} else if (is_substr(str, i, _("/i"))) {
|
} else if (is_substr(str, i, _("/i"))) {
|
||||||
tags.close(ret, italic);
|
tags.close(ret, italic);
|
||||||
} else if (is_substr(str, i, _("sym"))) {
|
} else if (is_substr(str, i, _("sym"))) {
|
||||||
tags.close(ret, symbol);
|
tags.open (ret, symbol);
|
||||||
} else if (is_substr(str, i, _("/sym"))) {
|
} else if (is_substr(str, i, _("/sym"))) {
|
||||||
if (!symbols.empty()) {
|
if (!symbols.empty()) {
|
||||||
// write symbols in a special way
|
// write symbols in a special way
|
||||||
tags.write_pending_tags(ret);
|
tags.write_pending_tags(ret);
|
||||||
ret += symbols_to_html(symbols, symbol_font);
|
ret += symbols_to_html(symbols, *symbol_font, symbol_size);
|
||||||
symbols.clear();
|
symbols.clear();
|
||||||
}
|
}
|
||||||
tags.close(ret, symbol);
|
tags.close(ret, symbol);
|
||||||
@@ -184,7 +238,7 @@ String to_html(const String& str_in, const SymbolFontP& symbol_font) {
|
|||||||
// end of input
|
// end of input
|
||||||
if (!symbols.empty()) {
|
if (!symbols.empty()) {
|
||||||
tags.write_pending_tags(ret);
|
tags.write_pending_tags(ret);
|
||||||
ret += symbols_to_html(symbols, symbol_font);
|
ret += symbols_to_html(symbols, *symbol_font, symbol_size);
|
||||||
symbols.clear();
|
symbols.clear();
|
||||||
}
|
}
|
||||||
tags.close_all(ret);
|
tags.close_all(ret);
|
||||||
@@ -194,8 +248,91 @@ String to_html(const String& str_in, const SymbolFontP& symbol_font) {
|
|||||||
// convert a tagged string to html
|
// convert a tagged string to html
|
||||||
SCRIPT_FUNCTION(to_html) {
|
SCRIPT_FUNCTION(to_html) {
|
||||||
SCRIPT_PARAM(String, input);
|
SCRIPT_PARAM(String, input);
|
||||||
SymbolFontP symbol_font; // TODO
|
// symbol font?
|
||||||
SCRIPT_RETURN(to_html(input, symbol_font));
|
SymbolFontP symbol_font;
|
||||||
|
SCRIPT_OPTIONAL_PARAM_N(String, _("symbol font"), font_name) {
|
||||||
|
symbol_font = SymbolFont::byName(font_name);
|
||||||
|
symbol_font->update(ctx);
|
||||||
|
}
|
||||||
|
SCRIPT_OPTIONAL_PARAM_N_(double, _("symbol font size"), symbol_font_size);
|
||||||
|
if (symbol_font_size <= 0) symbol_font_size = 12; // a default
|
||||||
|
SCRIPT_RETURN(to_html(input, symbol_font, symbol_font_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert a symbol string to html
|
||||||
|
SCRIPT_FUNCTION(symbols_to_html) {
|
||||||
|
SCRIPT_PARAM(String, input);
|
||||||
|
SCRIPT_PARAM_N(String, _("symbol font"), font_name);
|
||||||
|
SCRIPT_OPTIONAL_PARAM_N_(double, _("symbol font size"), symbol_font_size);
|
||||||
|
SymbolFontP symbol_font = SymbolFont::byName(font_name);
|
||||||
|
symbol_font->update(ctx);
|
||||||
|
if (symbol_font_size <= 0) symbol_font_size = 12; // a default
|
||||||
|
SCRIPT_RETURN(symbols_to_html(input, *symbol_font, symbol_font_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : BB Code
|
||||||
|
|
||||||
|
String to_bbcode(const String& str_in) {
|
||||||
|
String str = remove_tag_contents(str_in,_("<sep-soft"));
|
||||||
|
String ret;
|
||||||
|
Tag bold (_("[b]"), _("[/b]")),
|
||||||
|
italic(_("[i]"), _("[/i]"));
|
||||||
|
TagStack tags;
|
||||||
|
String symbols;
|
||||||
|
for (size_t i = 0 ; i < str.size() ; ) {
|
||||||
|
Char c = str.GetChar(i);
|
||||||
|
if (c == _('<')) {
|
||||||
|
++i;
|
||||||
|
if (is_substr(str, i, _("b"))) {
|
||||||
|
tags.open (ret, bold);
|
||||||
|
} else if (is_substr(str, i, _("/b"))) {
|
||||||
|
tags.close(ret, bold);
|
||||||
|
} else if (is_substr(str, i, _("i"))) {
|
||||||
|
tags.open (ret, italic);
|
||||||
|
} else if (is_substr(str, i, _("/i"))) {
|
||||||
|
tags.close(ret, italic);
|
||||||
|
} /*else if (is_substr(str, i, _("sym"))) {
|
||||||
|
tags.open (ret, symbol);
|
||||||
|
} else if (is_substr(str, i, _("/sym"))) {
|
||||||
|
if (!symbols.empty()) {
|
||||||
|
// write symbols in a special way
|
||||||
|
tags.write_pending_tags(ret);
|
||||||
|
ret += symbols_to_html(symbols, symbol_font);
|
||||||
|
symbols.clear();
|
||||||
|
}
|
||||||
|
tags.close(ret, symbol);
|
||||||
|
}*/
|
||||||
|
i = skip_tag(str, i-1);
|
||||||
|
} else {
|
||||||
|
// normal character
|
||||||
|
tags.write_pending_tags(ret);
|
||||||
|
++i;
|
||||||
|
// if (symbol.opened > 0 && symbol_font) {
|
||||||
|
// symbols += c; // write as symbols instead
|
||||||
|
// } else {
|
||||||
|
if (c == _('\1')) { // unescape <
|
||||||
|
ret += _("<");
|
||||||
|
} else {
|
||||||
|
ret += c;
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// end of input
|
||||||
|
/* if (!symbols.empty()) {
|
||||||
|
tags.write_pending_tags(ret);
|
||||||
|
ret += symbols_to_html(symbols, symbol_font);
|
||||||
|
symbols.clear();
|
||||||
|
}*/
|
||||||
|
tags.close_all(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert a tagged string to BBCode
|
||||||
|
SCRIPT_FUNCTION(to_bbcode) {
|
||||||
|
SCRIPT_PARAM(String, input);
|
||||||
|
throw "TODO";
|
||||||
|
// SCRIPT_RETURN(to_bbcode(input, symbol_font));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Text
|
// ----------------------------------------------------------------------------- : Text
|
||||||
@@ -208,14 +345,6 @@ SCRIPT_FUNCTION(to_text) {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Files
|
// ----------------------------------------------------------------------------- : Files
|
||||||
|
|
||||||
void guard_export_info(const String& fun) {
|
|
||||||
if (!export_info()) {
|
|
||||||
throw ScriptError(_("Can only use ") + fun + _(" from export templates"));
|
|
||||||
} else if (export_info()->directory_relative.empty()) {
|
|
||||||
throw ScriptError(_("Can only use ") + fun + _(" when 'create directory' is set to true"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy from source package -> destination directory, return new filename (relative)
|
// copy from source package -> destination directory, return new filename (relative)
|
||||||
SCRIPT_FUNCTION(copy_file) {
|
SCRIPT_FUNCTION(copy_file) {
|
||||||
guard_export_info(_("copy_file"));
|
guard_export_info(_("copy_file"));
|
||||||
@@ -252,21 +381,27 @@ SCRIPT_FUNCTION(write_text_file) {
|
|||||||
SCRIPT_FUNCTION(write_image_file) {
|
SCRIPT_FUNCTION(write_image_file) {
|
||||||
guard_export_info(_("write_image_file"));
|
guard_export_info(_("write_image_file"));
|
||||||
ExportInfo& ei = *export_info();
|
ExportInfo& ei = *export_info();
|
||||||
// get image
|
|
||||||
SCRIPT_PARAM(ScriptValueP, input);
|
|
||||||
ScriptObject<CardP>* card = dynamic_cast<ScriptObject<CardP>*>(input.get()); // is it a card?
|
|
||||||
Image image;
|
|
||||||
if (card) {
|
|
||||||
image = export_bitmap(ei.set, card->getValue()).ConvertToImage();
|
|
||||||
} else {
|
|
||||||
image = image_from_script(input)->generate(GeneratedImage::Options(0,0,ei.export_template.get(),ei.set.get()));
|
|
||||||
}
|
|
||||||
if (!image.Ok()) throw Error(_("Unable to convert .. to image"));
|
|
||||||
// filename
|
// filename
|
||||||
SCRIPT_PARAM(String, file); // file to write to
|
SCRIPT_PARAM(String, file); // file to write to
|
||||||
wxFileName fn;
|
wxFileName fn;
|
||||||
fn.SetPath(ei.directory_absolute);
|
fn.SetPath(ei.directory_absolute);
|
||||||
fn.SetFullName(file);
|
fn.SetFullName(file);
|
||||||
|
if (!ei.exported_images.insert(fn.GetFullName()).second) {
|
||||||
|
SCRIPT_RETURN(fn.GetFullName()); // already written an image with this name
|
||||||
|
}
|
||||||
|
// get image
|
||||||
|
SCRIPT_PARAM(ScriptValueP, input);
|
||||||
|
SCRIPT_OPTIONAL_PARAM_(int, width);
|
||||||
|
SCRIPT_OPTIONAL_PARAM_(int, height);
|
||||||
|
ScriptObject<CardP>* card = dynamic_cast<ScriptObject<CardP>*>(input.get()); // is it a card?
|
||||||
|
Image image;
|
||||||
|
GeneratedImage::Options options(width, height, ei.export_template.get(),ei.set.get());
|
||||||
|
if (card) {
|
||||||
|
image = conform_image(export_bitmap(ei.set, card->getValue()).ConvertToImage(), options);
|
||||||
|
} else {
|
||||||
|
image = image_from_script(input)->generateConform(options);
|
||||||
|
}
|
||||||
|
if (!image.Ok()) throw Error(_("Unable to generate image for file ") + file);
|
||||||
// write
|
// write
|
||||||
image.SaveFile(fn.GetFullPath());
|
image.SaveFile(fn.GetFullPath());
|
||||||
SCRIPT_RETURN(fn.GetFullName());
|
SCRIPT_RETURN(fn.GetFullName());
|
||||||
@@ -276,6 +411,7 @@ SCRIPT_FUNCTION(write_image_file) {
|
|||||||
|
|
||||||
void init_script_export_functions(Context& ctx) {
|
void init_script_export_functions(Context& ctx) {
|
||||||
ctx.setVariable(_("to html"), script_to_html);
|
ctx.setVariable(_("to html"), script_to_html);
|
||||||
|
ctx.setVariable(_("symbols to html"), script_symbols_to_html);
|
||||||
ctx.setVariable(_("to text"), script_to_text);
|
ctx.setVariable(_("to text"), script_to_text);
|
||||||
ctx.setVariable(_("copy file"), script_copy_file);
|
ctx.setVariable(_("copy file"), script_copy_file);
|
||||||
ctx.setVariable(_("write text file"), script_write_text_file);
|
ctx.setVariable(_("write text file"), script_write_text_file);
|
||||||
|
|||||||
+1
-30
@@ -61,36 +61,7 @@ Image ScriptableImage::generate(const GeneratedImage::Options& options, bool cac
|
|||||||
i.SetAlpha(0,0,0);
|
i.SetAlpha(0,0,0);
|
||||||
image = i;
|
image = i;
|
||||||
}
|
}
|
||||||
// resize?
|
image = conform_image(image, options);
|
||||||
int iw = image.GetWidth(), ih = image.GetHeight();
|
|
||||||
if ((iw == options.width && ih == options.height) || options.width == 0 || options.height == 0) {
|
|
||||||
// already the right size
|
|
||||||
} else if (options.preserve_aspect == ASPECT_FIT) {
|
|
||||||
// determine actual size of resulting image
|
|
||||||
int w, h;
|
|
||||||
if (iw * options.height > ih * options.width) { // too much height requested
|
|
||||||
w = options.width;
|
|
||||||
h = options.width * ih / iw;
|
|
||||||
} else {
|
|
||||||
w = options.height * iw / ih;
|
|
||||||
h = options.height;
|
|
||||||
}
|
|
||||||
Image resampled_image(w, h, false);
|
|
||||||
resample(image, resampled_image);
|
|
||||||
image = resampled_image;
|
|
||||||
} else {
|
|
||||||
Image resampled_image(options.width, options.height, false);
|
|
||||||
if (options.preserve_aspect == ASPECT_BORDER && (options.width < options.height * 3) && (options.height < options.width * 3)) {
|
|
||||||
// preserve the aspect ratio if there is not too much difference
|
|
||||||
resample_preserve_aspect(image, resampled_image);
|
|
||||||
} else {
|
|
||||||
resample(image, resampled_image);
|
|
||||||
}
|
|
||||||
image = resampled_image;
|
|
||||||
}
|
|
||||||
if (options.saturate) {
|
|
||||||
saturate(image, 40);
|
|
||||||
}
|
|
||||||
// cache? and return
|
// cache? and return
|
||||||
if (cache) cached = image;
|
if (cache) cached = image;
|
||||||
return image;
|
return image;
|
||||||
|
|||||||
@@ -263,3 +263,22 @@ bool cannocial_name_compare(const String& as, const Char* b) {
|
|||||||
a++; b++;
|
a++; b++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Filenames
|
||||||
|
|
||||||
|
bool is_filename_char(Char c) {
|
||||||
|
return isAlnum(c) || c == _(' ') || c == _('_') || c == _('-') || c == _('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
String clean_filename(const String& name) {
|
||||||
|
String clean;
|
||||||
|
FOR_EACH_CONST(c, name) {
|
||||||
|
if (is_filename_char(c)) {
|
||||||
|
clean += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (clean.empty() || starts_with(clean, _("."))) {
|
||||||
|
clean = _("no-name") + clean;
|
||||||
|
}
|
||||||
|
return clean;
|
||||||
|
}
|
||||||
@@ -172,5 +172,10 @@ bool is_substr(const String& str, size_t pos, const String& cmp);
|
|||||||
/// Compare two strings for equality, b may contain '_' where a contains ' '
|
/// Compare two strings for equality, b may contain '_' where a contains ' '
|
||||||
bool cannocial_name_compare(const String& a, const Char* b);
|
bool cannocial_name_compare(const String& a, const Char* b);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Filenames
|
||||||
|
|
||||||
|
/// Make sure a string is safe to use as a filename
|
||||||
|
String clean_filename(const String& name);
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : EOF
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user