Layout-TNG-Output.cpp revision 5c78d8fe18f4462beaac4afad2e21a2d8ee2be4c
/*
* Inkscape::Text::Layout - text layout engine output functions
*
* Authors:
* Richard Hughes <cyreve@users.sf.net>
*
* Copyright (C) 2005 Richard Hughes
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include <glib.h>
#include "Layout-TNG.h"
#include "display/drawing-text.h"
#include "style.h"
#include "print.h"
#include "font-instance.h"
#include "svg/svg-length.h"
#include "extension/internal/cairo-render-context.h"
#include "libunicode-convert/unicode-convert.h"
#endif
namespace Inkscape {
namespace Extension {
namespace Internal {
class CairoRenderContext;
class CairoGlyphInfo;
}
}
}
namespace Inkscape {
namespace Text {
/*
dx array (character widths) and
ky (vertical kerning for entire span)
are smuggled through to the EMF (ignored by others) as:
text<nul>N w1 w2 w3 ...wN<nul>y1 y2 y3 .. yN<nul><nul>
where the widths and y kern values are floats 7 characters wide, including the space
*/
/* holds: string
fake terminator (one \0)
Number of widths (ndxy)
series of widths (ndxy entries)
fake terminator (one \0)
y kern value (one float)
real terminator (two \0)
*/
cptr+=7;
for(int i=0; i<ndx ; i++){ // all the widths
cptr+=7;
}
cptr++; // second fake terminator
return(smuggle);
}
void Layout::_clearOutputObjects()
{
_paragraphs.clear();
_characters.clear();
_path_fitted = NULL;
}
{
}
{
} else {
}
}
{
int glyph_index = 0;
InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[_spans[span_index].in_input_stream_item]);
while (glyph_index < (int)_glyphs.size() && _characters[_glyphs[glyph_index].in_character].in_span == span_index) {
}
glyph_index++;
}
}
}
{
if (length != -1) {
if (start == -1)
start = 0;
}
// this could be faster
if (glyph_rect) {
}
}
}
return bbox;
}
{
int doUTN=0;
int lasttarget=0;
int newtarget=0;
#define MAX_DX 2048
float hold_dx[MAX_DX]; // For smuggling dx values (character widths) into print functions, unlikely any simple text output will be longer than this.
float ky; // For smuggling y kern value for span
int ndx=0;
if (_input_stream.empty()) return;
// invisible glyphs
glyph_index++;
continue;
}
InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
if (text_to_path || _path_fitted) {
if (pv) {
}
glyph_index++;
} else {
Geom::Point g_pos(0,0); // all strings are output at (0,0) because we do the translation using the matrix
// since we're outputting character codes, not glyphs, we want the character x
glyph_matrix[5] = span.chunk(this).left_x + span.x_start + _characters[_glyphs[glyph_index].in_character].x;
} else {
glyph_matrix[4] = span.chunk(this).left_x + span.x_start + _characters[_glyphs[glyph_index].in_character].x;
}
char_index--;
span_iter++;
}
// try to output as many characters as possible in one go by detecting kerning and stopping when we encounter it
// also break spans at changes in Unicode->nonunicode translations, so that each span
// sent down from here is translated the same way. The translation happens much later
// in the emf-print code.
// Note that the incoming stream has a predefined notion of what is in each "span" and it is not
// entirely clear why. For instance, the string "%%% text %%%%", where % is the Unicode "Sagittarius"
// character has 3 spans, with the first two ending on the spaces. Yet in the XML there is only one tspan.
// Consequently when the Unicode->NonUnicode detection is on the first space will go out by itself,
// because it is at the end of a span, whereas the second space goes with the "text".
do {
/*
std::cout << "glyph info at:" << glyph_index
<< " glyphNo:" << _glyphs[glyph_index].glyph
<< " in_character:" << _glyphs[glyph_index].in_character
<< " x:" << _glyphs[glyph_index].x
<< " y:" << _glyphs[glyph_index].y
<< " rotation:" << _glyphs[glyph_index].rotation
<< " width:" << _glyphs[glyph_index].width
<< std::endl;
*/
span_string += *span_iter;
span_iter++;
}
else { // silently truncate any text line silly enough to be longer than MAX_DX
break;
}
glyph_index++;
}
&& _path_fitted == NULL
);
// the dx array is smuggled through to the EMF (ignored by others) as:
// text<nul>w1 w2 w3 ...wn<nul><nul>
// where the widths are floats 7 characters wide, including the space
// sp_print_text(ctx, span_string.c_str(), g_pos, text_source->style);
ndx=0;
}
}
}
#ifdef HAVE_CAIRO_PDF
{
if (_input_stream.empty()) return;
bool clip_mode = false;//(ctx->getRenderMode() == CairoRenderContext::RENDER_MODE_CLIP);
// invisible glyphs
glyph_index++;
continue;
}
InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
if (clip_mode) {
if (pathv) {
}
glyph_index++;
continue;
}
font_matrix[4] = 0;
font_matrix[5] = 0;
char_index--;
span_iter++;
}
// try to output as many characters as possible in one go
unsigned int first_index = glyph_index;
do {
span_string += *span_iter;
span_iter++;
if (glyph_index != first_index)
// this is the translation for x,y-offset
glyph_index++;
}
&& _path_fitted == NULL
// remove vertical flip
if (opacity != 1.0) {
}
if (glyph_index - first_index > 0)
if (opacity != 1.0) {
}
}
}
#endif
// these functions are for dumpAsText() only. No need to translate
{
switch (d) {
}
return "???";
}
static char const *style_to_text(PangoStyle s)
{
switch (s) {
case PANGO_STYLE_NORMAL: return "upright";
case PANGO_STYLE_ITALIC: return "italic";
case PANGO_STYLE_OBLIQUE: return "oblique";
}
return "???";
}
static char const *weight_to_text(PangoWeight w)
{
switch (w) {
case PANGO_WEIGHT_THIN : return "thin";
case PANGO_WEIGHT_ULTRALIGHT: return "ultralight";
case PANGO_WEIGHT_LIGHT : return "light";
case PANGO_WEIGHT_BOOK : return "book";
case PANGO_WEIGHT_NORMAL : return "normalweight";
case PANGO_WEIGHT_MEDIUM : return "medium";
case PANGO_WEIGHT_SEMIBOLD : return "semibold";
case PANGO_WEIGHT_BOLD : return "bold";
case PANGO_WEIGHT_ULTRABOLD : return "ultrabold";
case PANGO_WEIGHT_HEAVY : return "heavy";
case PANGO_WEIGHT_ULTRAHEAVY: return "ultraheavy";
}
return "???";
}
{
return "";
}
return "";
}
{
char line[256];
snprintf(line, sizeof(line), " in para %d (direction=%s)\n", _lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph,
direction_to_text(_paragraphs[_lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph].base_direction));
snprintf(line, sizeof(line), " in source %d (type=%d, cookie=%p)\n", _spans[span_index].in_input_stream_item,
snprintf(line, sizeof(line), " in line %d (baseline=%f, shape=%d)\n", _chunks[_spans[span_index].in_chunk].in_line,
snprintf(line, sizeof(line), " in chunk %d (x=%f, baselineshift=%f)\n", _spans[span_index].in_chunk, _chunks[_spans[span_index].in_chunk].left_x, _spans[span_index].baseline_shift);
snprintf(line, sizeof(line), " font '%s' %f %s %s\n", pango_font_description_get_family(_spans[span_index].font->descr), _spans[span_index].font_size, style_to_text(pango_font_description_get_style(_spans[span_index].font->descr)), weight_to_text(pango_font_description_get_weight(_spans[span_index].font->descr)));
}
snprintf(line, sizeof(line), " x_start = %f, x_end = %f\n", _spans[span_index].x_start, _spans[span_index].x_end);
snprintf(line, sizeof(line), " line height: ascent %f, descent %f leading %f\n", _spans[span_index].line_height.ascent, _spans[span_index].line_height.descent, _spans[span_index].line_height.leading);
snprintf(line, sizeof(line), " direction %s, block-progression %s\n", direction_to_text(_spans[span_index].direction), direction_to_text(_spans[span_index].block_progression));
result += " ** characters:\n";
// very inefficent code. what the hell, it's only debug stuff.
snprintf(line, sizeof(line), " %d: control x=%f flags=%03x glyph=%d\n", char_index, _characters[char_index].x, *u.uattr, _characters[char_index].in_glyph);
} else {
snprintf(line, sizeof(line), " %d: '%c' x=%f flags=%03x glyph=%d\n", char_index, *iter_char, _characters[char_index].x, *u.uattr, _characters[char_index].in_glyph);
iter_char++;
}
}
result += " ** glyphs:\n";
snprintf(line, sizeof(line), " %d: %d (%f,%f) rot=%f cx=%f char=%d\n", glyph_index, _glyphs[glyph_index].glyph, _glyphs[glyph_index].x, _glyphs[glyph_index].y, _glyphs[glyph_index].rotation, _glyphs[glyph_index].width, _glyphs[glyph_index].in_character);
}
result += "\n";
}
result += "EOT\n";
return result;
}
{
double offset = 0.0;
if (startOffset._set) {
else
}
case CENTER:
break;
case RIGHT:
offset -= _getChunkWidth(0);
break;
default:
break;
}
if (_characters.empty()) {
int unused = 0;
} else {
}
}
}
for (next_cluster_char_index = char_index + 1 ; next_cluster_char_index < _characters.size() ; next_cluster_char_index++) {
if (_characters[next_cluster_char_index].in_glyph != -1 && _characters[next_cluster_char_index].char_attributes.is_cursor_position)
{
break;
}
}
} else {
}
double cluster_width = 0.0;
for (size_t glyph_index = current_cluster_glyph_index ; glyph_index < next_cluster_glyph_index ; glyph_index++)
{
}
// TODO block progression?
{
}
int unused = 0;
// as far as I know these functions are const, they're just not marked as such
Path::cut_position *midpoint_otp = const_cast<Path&>(path).CurvilignToPosition(1, &midpoint_offset, unused);
const_cast<Path&>(path).PointAndTangentAt(midpoint_otp[0].piece, midpoint_otp[0].t, midpoint, tangent);
Path::cut_position *start_otp = const_cast<Path&>(path).CurvilignToPosition(1, &start_offset, unused);
bool on_same_subpath = true;
on_same_subpath = false;
break;
}
}
if (on_same_subpath) {
// both points were on the same subpath (without this test the angle is very weird)
if (endpoint != startpoint) {
} else {
}
}
}
}
}
for (size_t glyph_index = current_cluster_glyph_index; glyph_index < next_cluster_glyph_index ; glyph_index++) {
_glyphs[glyph_index].x = midpoint[Geom::Y] - tangent[Geom::X] * _glyphs[glyph_index].y - span.chunk(this).left_x;
_glyphs[glyph_index].y = midpoint[Geom::X] + tangent[Geom::Y] * _glyphs[glyph_index].y - _lines.front().baseline_y;
}
} else {
for (size_t glyph_index = current_cluster_glyph_index; glyph_index < next_cluster_glyph_index ; glyph_index++) {
double tangent_shift = -cluster_width * 0.5 + _glyphs[glyph_index].x - (_characters[char_index].x + span.x_start);
{
}
_glyphs[glyph_index].x = midpoint[Geom::X] + tangent[Geom::X] * tangent_shift - tangent[Geom::Y] * _glyphs[glyph_index].y - span.chunk(this).left_x;
_glyphs[glyph_index].y = midpoint[Geom::Y] + tangent[Geom::Y] * tangent_shift + tangent[Geom::X] * _glyphs[glyph_index].y - _lines.front().baseline_y;
}
}
_input_truncated = false;
} else { // outside the bounds of the path: hide the glyphs
_input_truncated = true;
}
}
}
_path_fitted = &path;
}
{
for (int glyph_index = from_glyph._glyph_index ; glyph_index < to_glyph._glyph_index ; glyph_index++) {
if (pathv) {
}
}
if ( cc ) {
} else {
}
while (cc) {
/* fixme: This is dangerous, as we are mixing art_alloc and g_new */
}
return curve;
}
{
// this is all massively oversimplified
// I can't actually think of anybody who'll want to use it at the moment, so it'll stay simple
}
}
}//namespace Text
}//namespace Inkscape
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :