...
 
Commits (7)
......@@ -128,8 +128,7 @@ CairoRenderContext::CairoRenderContext(CairoRenderer *parent) :
_renderer(parent),
_render_mode(RENDER_MODE_NORMAL),
_clip_mode(CLIP_MODE_MASK),
_omittext_state(EMPTY),
_omittext_missing_pages(0)
_omittext_state(EMPTY)
{
}
......@@ -885,19 +884,16 @@ CairoRenderContext::finish(bool finish_surface)
if (_vector_based_target && finish_surface)
cairo_show_page(_cr);
// PDF+TeX Output, see CairoRenderContext::_prepareRenderGraphic()
while (_omittext_missing_pages > 0) {
_omittext_missing_pages--;
g_warning("PDF+TeX output: issuing blank PDF page at end (workaround for previous error)");
cairo_show_page(_cr);
}
cairo_status_t status = cairo_status(_cr);
if (status != CAIRO_STATUS_SUCCESS)
g_critical("error while rendering output: %s", cairo_status_to_string(status));
cairo_destroy(_cr);
_cr = NULL;
if (finish_surface)
cairo_surface_finish(_surface);
cairo_status_t status = cairo_surface_status(_surface);
status = cairo_surface_status(_surface);
cairo_surface_destroy(_surface);
_surface = NULL;
......@@ -1445,28 +1441,38 @@ CairoRenderContext::_prepareRenderGraphic()
{
// Only PDFLaTeX supports importing a single page of a graphics file,
// so only PDF backend gets interleaved text/graphics
if (_is_omittext && _target == CAIRO_SURFACE_TYPE_PDF) {
if (_is_omittext && _target == CAIRO_SURFACE_TYPE_PDF && _render_mode != RENDER_MODE_CLIP) {
if (_omittext_state == NEW_PAGE_ON_GRAPHIC) {
if (cairo_get_group_target(_cr) != cairo_get_target(_cr)) {
// we are in the middle of a group, i. e., between cairo_push_group() and cairo_pop_group().
// cairo_show_page() has no effect here!
// To ensure that the the generated TeX source doesn't try to include non-existing pages,
// we will later output an extra blank page.
// This is a workaround for bug #1417470.
g_warning("PDF+TeX output: Found text inside a clipped/masked group. This is not supported, the Z-order will be incorrect. Blank pages will be added to the PDF output to work around bug #1417470.");
_omittext_missing_pages++;
} else {
// no group is active, create new page
cairo_show_page(_cr);
// Output missing pages (workaround for the 'if' case above).
// With this solution, the Z-order is more wrong than necessary.
// It would be better to print the blank pages first, and then the actual current page.
// However, this isn't easily possible with cairo.
while (_omittext_missing_pages > 0) {
_omittext_missing_pages--;
g_warning("PDF+TeX output: issuing blank PDF page (workaround for previous error)");
cairo_show_page(_cr);
}
// better set this immediately (not sure if masks applied during "popLayer" could call
// this function, too, triggering the same code again in error
_omittext_state = GRAPHIC_ON_TOP;
// As we can not emit the page in the middle of a layer (aka group) - it will not be fully painted yet! -
// the following basically mirrors the calls in CairoRenderer::renderItem (but in reversed order)
// - first traverse all saved states in reversed order (i.e. from deepest nesting to the top)
// and apply clipping / masking to layers on the way (this is done in popLayer)
// - then emit the page using cairo_show_page()
// - finally restore the previous state with proper transforms and appropriate layers again
//
// TODO: While this appears to be an ugly hack it seems to work
// Somebody with a more intimate understanding of cairo and the renderer implementation might
// be able to implement this in a cleaner way, though.
int stack_size = g_slist_length(_state_stack);
for (int i = 0; i < stack_size-1; i++) {
if (static_cast<CairoRenderState *>(g_slist_nth_data(_state_stack, i))->need_layer)
popLayer();
cairo_restore(_cr);
_state = static_cast<CairoRenderState *>(g_slist_nth_data(_state_stack, i+1));
}
cairo_show_page(_cr);
for (int i = stack_size-2; i >= 0; i--) {
cairo_save(_cr);
_state = static_cast<CairoRenderState *>(g_slist_nth_data(_state_stack, i));
if (_state->need_layer)
pushLayer();
setTransform(_state->transform);
}
}
_omittext_state = GRAPHIC_ON_TOP;
......
......@@ -207,7 +207,6 @@ protected:
CairoClipMode _clip_mode;
CairoOmitTextPageState _omittext_state;
int _omittext_missing_pages;
cairo_pattern_t *_createPatternForPaintServer(SPPaintServer const *const paintserver,
Geom::OptRect const &pbox, float alpha);
......
......@@ -259,6 +259,11 @@ void LaTeXTextRenderer::sp_use_render(SPUse *use)
void LaTeXTextRenderer::sp_text_render(SPText *textobj)
{
// Nothing to do here... (so don't emit an empty box)
// Also avoids falling out of sync with the CairoRenderer (which won't render anything in this case either)
if (textobj->layout.getActualLength() == 0)
return;
// Only PDFLaTeX supports importing a single page of a graphics file,
// so only PDF backend gets interleaved text/graphics
if (_pdflatex && _omittext_state == GRAPHIC_ON_TOP)
......@@ -270,16 +275,20 @@ void LaTeXTextRenderer::sp_text_render(SPText *textobj)
// Align vertically on the baseline of the font (retreived from the anchor point)
// Align horizontally on anchorpoint
gchar const *alignment = NULL;
gchar const *alignstack = NULL;
switch (style->text_anchor.computed) {
case SP_CSS_TEXT_ANCHOR_START:
alignment = "[lb]";
alignment = "[lt]";
alignstack = "[l]";
break;
case SP_CSS_TEXT_ANCHOR_END:
alignment = "[rb]";
alignment = "[rt]";
alignstack = "[r]";
break;
case SP_CSS_TEXT_ANCHOR_MIDDLE:
default:
alignment = "[b]";
alignment = "[t]";
alignstack = "[c]";
break;
}
Geom::Point anchor = textobj->attributes.firstXY() * transform();
......@@ -326,39 +335,16 @@ void LaTeXTextRenderer::sp_text_render(SPText *textobj)
os << "\\rotatebox{" << degrees << "}{";
}
os << "\\makebox(0,0)" << alignment << "{";
os << "\\smash{"; // smash the text, to be able to put the makebox coordinates at the baseline
os << "\\shortstack" << alignstack << "{";
os << "\\smash{";
bool smash_closed = false;
// Walk through all spans in the text object.
// Write span strings to LaTeX, associated with font weight and style.
Inkscape::Text::Layout const &layout = *(te_get_layout (textobj));
for (Inkscape::Text::Layout::iterator li = layout.begin(), le = layout.end();
for (Inkscape::Text::Layout::iterator li = layout.begin(), le = layout.end();
li != le; li.nextStartOfSpan())
{
SPStyle const &spanstyle = *(sp_te_style_at_position (textobj, li));
bool is_bold = false, is_italic = false, is_oblique = false;
if (spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_500 ||
spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_600 ||
spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_700 ||
spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_800 ||
spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_900 ||
spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_BOLD ||
spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_BOLDER)
{
is_bold = true;
os << "\\textbf{";
}
if (spanstyle.font_style.computed == SP_CSS_FONT_STYLE_ITALIC)
{
is_italic = true;
os << "\\textit{";
}
if (spanstyle.font_style.computed == SP_CSS_FONT_STYLE_OBLIQUE)
{
is_oblique = true;
os << "\\textsl{"; // this is an accurate choice if the LaTeX chosen font matches the font in Inkscape. Gives bad results when it is not so...
}
Inkscape::Text::Layout::iterator ln = li;
ln.nextStartOfSpan();
Glib::ustring uspanstr = sp_te_get_string_multiline (textobj, li, ln);
......@@ -366,22 +352,61 @@ void LaTeXTextRenderer::sp_text_render(SPText *textobj)
if (!spanstr) {
continue;
}
bool is_bold = false, is_italic = false, is_oblique = false;
// newline character only -> don't attempt to add style (will break compilation in LaTeX)
if (g_strcmp0(spanstr, "\n")) {
SPStyle const &spanstyle = *(sp_te_style_at_position (textobj, li));
if (spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_500 ||
spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_600 ||
spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_700 ||
spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_800 ||
spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_900 ||
spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_BOLD ||
spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_BOLDER)
{
is_bold = true;
os << "\\textbf{";
}
if (spanstyle.font_style.computed == SP_CSS_FONT_STYLE_ITALIC)
{
is_italic = true;
os << "\\textit{";
}
if (spanstyle.font_style.computed == SP_CSS_FONT_STYLE_OBLIQUE)
{
is_oblique = true;
os << "\\textsl{"; // this is an accurate choice if the LaTeX chosen font matches the font in Inkscape. Gives bad results when it is not so...
}
}
// replace carriage return with double slash
gchar ** splitstr = g_strsplit(spanstr, "\n", -1);
gchar *spanstr_new = g_strjoinv("\\\\ ", splitstr);
os << spanstr_new;
gchar ** splitstr = g_strsplit(spanstr, "\n", 2);
os << splitstr[0];
// smash the first line of the text only, to be able to align the rest of the makebox top
// assuming that spans always end at the end of a line
if (g_strv_length(splitstr) > 1)
{
if (!smash_closed)
{
os << "}"; // smash end
smash_closed = true;
}
os << "\\\\";
}
g_strfreev(splitstr);
g_free(spanstr_new);
if (is_oblique) { os << "}"; } // oblique end
if (is_italic) { os << "}"; } // italic end
if (is_bold) { os << "}"; } // bold end
}
os << "}"; // smash end
if (has_rotation) {
os << "}"; // rotatebox end
}
if (!smash_closed) { os << "}"; } // smash end
os << "}"; // shortstack end
if (has_rotation) { os << "}"; } // rotatebox end
os << "}"; //makebox end
os << "}%\n"; // put end
......