Layout-TNG-Output.cpp revision 9a8ade3834e2a67c1d95bcfc900ab36be37a03a5
/*
* 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"
namespace Inkscape {
namespace Extension {
namespace Internal {
class CairoRenderContext;
}
}
}
namespace Inkscape {
namespace Text {
/*
dx array (character widths) and
ky (vertical kerning for entire span)
rtl (+1 for LTR, -1 RTL)
are smuggled through to the EMF (ignored by others) as:
text<nul>N w1 w2 w3 ...wN<nul>y1 y2 y3 .. yN<nul><nul>
The ndx, widths, y kern, and rtl are all 7 characters wide. ndx and rtl are ints, the widths and ky are
formatted as ' 6f'.
*/
/* holds: string
fake terminator (one \0)
Number of widths (ndx)
series of widths (ndx entries)
fake terminator (one \0)
y kern value (one float)
rtl value (one float)
real terminator (two \0)
*/
for(int i=0; i<ndx ; i++){ // all the widths
}
*cptr='\0';
cptr++; // second fake terminator
*cptr = '\0';
return(smuggle);
}
void Layout::_clearOutputObjects()
{
_paragraphs.clear();
_characters.clear();
_path_fitted = NULL;
}
{
}
{
} else {
}
}
{
int glyph_index = 0;
double phase0 = 0.0;
InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[_spans[span_index].in_input_stream_item]);
if(!span_index ||
}
else {
}
}
else {
}
_spans[span_index].font->FontDecoration(underline_position, underline_thickness, line_through_position, line_through_thickness);
}
else { // can this case ever occur?
}
bool first_line_glyph = true;
while (glyph_index < (int)_glyphs.size() && _characters[_glyphs[glyph_index].in_character].in_span == span_index) {
first_line_glyph = false;
}
// save the starting coordinates for the line - these are needed for figuring out dot/dash/wave phase
);
}
glyph_index++;
}
}
}
{
if (length != -1) {
if (start == -1)
start = 0;
}
// this could be faster
if (glyph_rect) {
}
}
}
return bbox;
}
/* This version is much simpler than the old one
*/
{
int oldtarget = 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;
if (text_to_path || _path_fitted) {
InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
if (pv) {
}
}
}
else {
/* index by characters, referencing glyphs and spans only as needed */
double char_x;
Geom::Point g_pos(0,0); // all strings are output at (0,0) because we do the translation using the matrix
if(glyph_index == -1){ // if the character maps to an invisible glyph we cannot know its geometry, so skip it and move on
char_index++;
continue;
}
char_x = 0.0;
InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
// 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;
}
case Layout::TOP_TO_BOTTOM:
case Layout::BOTTOM_TO_TOP:
}
if(doUTN)oldtarget=SingleUnicodeToNon(*text_iter); // this should only ever be with a 1:1 glyph:character situation
// accumulate a record to write
unsigned lc_index = char_index;
while(1){
lc_index++;
break;
}
// always append if here
text_string += *text_iter;
// figure out char widths, used by EMF, not currently used elsewhere
double cwidth;
if(lc_index == _glyphs[glyph_index].in_character){ // Glyph width is used only for the first character, these may be 0
}
else {
cwidth = 0;
}
/*
std:: cout << "DEBUG Layout::print in while "
<< " char_index " << char_index
<< " lc_index " << lc_index
<< " character " << std::hex << (int) *text_iter << std::dec
<< " glyph_index " << glyph_index
<< " glyph_xy " << _glyphs[glyph_index].x << " , " << _glyphs[glyph_index].y
<< " span_index " << span_index
<< " hold_iisi " << hold_iisi
<< std::endl; //DEBUG
*/
}
else { // silently truncate any text line silly enough to be longer than MAX_DX
break;
}
// conditions that prevent this character from joining the record
lc_index++;
if(lc_index >= _characters.size()) break; // nothing more to process, so it must be the end of the record
text_iter++;
if(doUTN)newtarget=SingleUnicodeToNon(*text_iter); // this should only ever be with a 1:1 glyph:character situation
// MUST exit on any major span change, but not on some little events, like a font substitution event irrelvant for the file save
if(span_index != next_span_index){
/* on major changes break out of loop.
1st case usually indicates an entire input line has been processed (out of several in a paragraph)
*/
/*
std:: cout << "DEBUG Layout::print in while --- "
<< " char_index " << char_index
<< " lc_index " << lc_index
<< " cwidth " << cwidth
<< " _char.x (next) " << (lc_index < _characters.size() ? _characters[lc_index].x : -1)
<< " char_x (end this)" << char_x
<< " diff " << fabs(char_x - _characters[lc_index].x)
<< " oldy " << ky
<< " nexty " << _glyphs[_characters[lc_index].in_glyph].y
<< std::endl; //DEBUG
*/
if(hold_iisi != _spans[next_span_index].in_input_stream_item)break; // major change, font, size, color, etc, must exit
/*
None of the above? Then this is a minor "pangito", update span_index and keep going.
The font used by the display may have failed over, but print does not care and can continue to use
whatever was specified in the XML.
*/
}
}
// write it
// the dx array is smuggled through to the EMF driver (ignored by others) as:
// text<nul>w1 w2 w3 ...wn<nul><nul>
// where the widths are floats 7 characters wide, including the space
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];
unsigned lastspan=5000;
for(unsigned j = 0; j < _characters.size() ; j++){
}
snprintf(line, sizeof(line), "char %4d: '%c' 0x%4.4x x=%8.4f glyph=%3d span=%3d\n", j, *icc, *icc, _characters[j].x, _characters[j].in_glyph, _characters[j].in_span);
icc++;
}
}
j, _glyphs[j].glyph, _glyphs[j].x, _glyphs[j].y, _glyphs[j].rotation, _glyphs[j].width, _glyphs[j].in_character);
}
}
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", sp_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 { // some text has empty tspans, iter_char cannot be dereferenced
snprintf(line, sizeof(line), " %d: '%c' 0x%4.4x x=%f flags=%03x glyph=%d\n", char_index, *iter_char, *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 :