Example: Changed glyph-outline.shape for use with FontConfig.
[shapes.git] / source / texttypes.cc
blobb5ff4ae2299a3693d4f0a128f9182e810bd409a7
1 /* This file is part of Shapes.
3 * Shapes is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * any later version.
8 * Shapes is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with Shapes. If not, see <http://www.gnu.org/licenses/>.
16 * Copyright 2008, 2010 Henrik Tidefelt
19 #include "Shapes_Helpers_decls.h"
21 #include "texttypes.h"
22 #include "dynamicenvironment.h"
23 #include "lighttypes.h"
24 #include "ast.h"
25 #include "astvar.h"
26 #include "isnan.h"
27 #include "globals.h"
28 #include "autoonoff.h"
29 #include "afmscanner.h"
30 #include "charconverters.h"
31 #include "constructorrepresentation.h"
32 #include "pagecontentstates.h"
33 #include "methodbase.h"
34 #include "shapescore.h"
35 #include "config.h"
37 #include <fstream>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <stdint.h>
41 #include <cstddef>
42 #include <iomanip>
44 #ifdef HAVE_FT2
45 #include FT_OUTLINE_H
46 #endif
48 using namespace Shapes;
50 std::map< RefCountPtr< const char >, RefCountPtr< SimplePDF::PDF_Object >, charRefPtrLess > Lang::Font::theFontResourceMap_;
51 std::map< RefCountPtr< const char >, RefCountPtr< const FontMetrics::FontMetric >, charRefPtrLess > Lang::Font::theFontMetricsMap_;
52 std::list< std::string > Lang::Font::theFontMetricsSearchPath_;
54 namespace Shapes
56 namespace Helpers
58 Kernel::StructureFactory FontMethod_glyph_resultFactory( "painter", "paths" );
62 SimplePDF::PDF_ToUnicode::PDF_ToUnicode( RefCountPtr< CharSet > charSet, const RefCountPtr< const Shapes::Lang::Font > & font )
63 : charSet_( charSet ),
64 font_( font )
66 dic[ "Filter" ] = SimplePDF::newName( "FlateDecode" );
69 SimplePDF::PDF_ToUnicode::~PDF_ToUnicode( )
70 { }
72 void
73 SimplePDF::PDF_ToUnicode::writeTo( std::ostream & os, SimplePDF::PDF_xref * xref, const RefCountPtr< const PDF_Object > & self ) const
75 std::ostringstream & wdata = const_cast< std::ostringstream & >( data );
76 wdata << std::setfill( '0' );
77 wdata << "/CIDInit /ProcSet findresource begin" << std::endl
78 << "12 dict begin" << std::endl
79 << "begincmap" << std::endl
80 << "/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def" << std::endl
81 << "/CMapName /Adobe-Identity-UCS def" << std::endl
82 << "/CMapType 2 def" << std::endl
83 << "1 begincodespacerange" << std::endl
84 << "<0000> <FFFF>" << std::endl
85 << "endcodespacerange" << std::endl ;
86 const size_t bufSize = 10;
87 unsigned char buf[ bufSize ];
88 size_t remaining = charSet_->size( );
89 size_t room = std::min( static_cast< size_t >( 100 ), remaining );
90 remaining -= room;
91 wdata << room << " beginbfchar" << std::endl ;
92 for( CharSet::const_iterator i = charSet_->begin( ); i != charSet_->end( ); ++i, --room )
94 if( room == 0 )
96 wdata << "endbfchar" << std::endl ;
97 room = std::min( static_cast< size_t >( 100 ), remaining );
98 remaining -= room;
99 wdata << room << " beginbfchar" << std::endl ;
101 size_t out_avail = 2;
102 unsigned char * out_buf = buf;
103 font_->encode( *i, reinterpret_cast< char ** >( & out_buf ), & out_avail );
104 if( out_avail != 0 )
106 throw Exceptions::InternalError( "Expected the encoded character to occupy exactly two bytes." );
108 wdata << "<" << std::hex << std::setw( 2 ) << static_cast< int >( buf[0] ) << std::hex << std::setw( 2 ) << static_cast< int >( buf[1] ) << "> " ;
109 out_avail = bufSize;
110 out_buf = buf;
111 i->encode_UTF16BE( reinterpret_cast< char ** >( & out_buf ), & out_avail );
112 wdata << "<" ;
113 for( const unsigned char * c = buf; c != out_buf; ++c )
115 wdata << std::hex << std::setw( 2 ) << static_cast< int >( *c ) ;
117 wdata << ">" << std::endl ;
119 wdata << "endbfchar" << std::endl
120 << "endcmap" << std::endl
121 << "CMapName currentdict /CMap defineresource pop" << std::endl
122 << "end" << std::endl
123 << "end" ; /* No need to put a newline at the end. */
124 PDF_Stream_out::writeTo( os, xref, self );
127 SimplePDF::PDF_Widths::PDF_Widths( RefCountPtr< CharSet > charSet, const RefCountPtr< const Shapes::Lang::Font > & font )
128 : charSet_( charSet ),
129 font_( font )
132 SimplePDF::PDF_Widths::~PDF_Widths( )
135 void
136 SimplePDF::PDF_Widths::writeTo( std::ostream & os, SimplePDF::PDF_xref * xref, const RefCountPtr< const PDF_Object > & self ) const
138 const FontMetrics::WritingDirectionMetrics * metrics = font_->metrics( )->horizontalMetrics_.getPtr( );
139 const size_t bufSize = 2;
140 unsigned char buf[ bufSize ];
141 VecType & wvec = const_cast< VecType & >( vec );
142 FontMetrics::CharacterMetrics m( 0 );
143 for( CharSet::const_iterator i = charSet_->begin( ); i != charSet_->end( ); )
145 CharSet::const_iterator j = i;
146 CharSet::const_iterator last = i;
147 ++i;
148 for( ; i != charSet_->end( ) && i->get_UCS4( ) == last->get_UCS4( ) + 1; last = i, ++i )
150 size_t out_avail = bufSize;
151 unsigned char * out_buf = buf;
152 font_->encode( j->get_UCS4( ), reinterpret_cast< char ** >( & out_buf ), & out_avail );
153 if( out_avail != 0 )
155 throw Exceptions::InternalError( "Expected the encoded character to occupy exactly two bytes." );
157 wvec.push_back( SimplePDF::newInt( static_cast< SimplePDF::PDF_Int::ValueType >( buf[0] ) * 0x100 +
158 static_cast< SimplePDF::PDF_Int::ValueType >( buf[1] ) ) );
159 SimplePDF::PDF_Vector * sub_vec = new SimplePDF::PDF_Vector;
160 wvec.push_back( RefCountPtr< SimplePDF::PDF_Object >( sub_vec ) );
161 for( ; j != i; ++j )
163 sub_vec->vec.push_back( SimplePDF::newFloat( metrics->charByCode( j->get_UCS4( ), & m )->horizontalCharWidthX_* 1000 ) );
166 PDF_Vector::writeTo( os, xref, self );
169 namespace Shapes
172 namespace Lang
175 class FontMethod_glyph : public Lang::MethodBase< Lang::Font >
177 public:
178 FontMethod_glyph( RefCountPtr< const Lang::Font > _self, const Ast::FileID * fullMethodID );
179 virtual ~FontMethod_glyph( );
180 virtual void call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const;
181 static const char * staticFieldID( ) { return "glyph"; }
187 void
188 Font_register_methods( Lang::SystemFinalClass * dstClass )
190 dstClass->registerMethod( new Kernel::MethodFactory< Lang::Font, Lang::FontMethod_glyph >( ) );
193 void
194 Lang::Font::push_backFontMetricsPath( const std::string & path )
196 if( path.empty( ) ||
197 path[ path.size( ) - 1 ] == '/' )
199 theFontMetricsSearchPath_.push_back( path );
201 else
203 theFontMetricsSearchPath_.push_back( path + "/" );
207 std::string
208 Lang::Font::searchGlyphList( )
210 std::string res;
212 if( theFontMetricsSearchPath_.empty( ) )
214 throw Exceptions::ExternalError( strrefdup( "The font metrics path was not set up (needed for the glyph list). Consider defining the environment variable SHAPESFONTMETRICS." ) );
217 typedef typeof theFontMetricsSearchPath_ ListType;
218 for( ListType::const_iterator i = theFontMetricsSearchPath_.begin( ); i != theFontMetricsSearchPath_.end( ); ++i )
220 res = *i + "glyphlist.txt";
221 struct stat theStatDummy;
222 if( stat( res.c_str( ), & theStatDummy ) == 0 )
224 return res;
227 throw Exceptions::MiscellaneousRequirement( "A font operation required the glyph list, but it was not found. It should be named \"glyphlist.txt\" and reside in a directory on the font metrics path. Please refer to your environment variable SHAPESFONTMETRICS." );
230 std::string
231 Lang::Font::searchFontMetrics( RefCountPtr< const char > fontName )
233 std::string res;
235 if( strlen( fontName.getPtr( ) ) == 0 )
237 throw Exceptions::InternalError( strrefdup( "Lang::Font::searchFontMetrics called with empty argument." ) );
240 if( *fontName.getPtr( ) == '/' )
242 throw Exceptions::InternalError( "The font name cannot begin with \"/\", as if is was an absolute path to something." );
245 if( theFontMetricsSearchPath_.empty( ) )
247 throw Exceptions::ExternalError( strrefdup( "The font metrics path was not set up. Consider defining the environment variable SHAPESFONTMETRICS." ) );
250 typedef typeof theFontMetricsSearchPath_ ListType;
251 for( ListType::const_iterator i = theFontMetricsSearchPath_.begin( ); i != theFontMetricsSearchPath_.end( ); ++i )
253 res = *i + fontName.getPtr( ) + ".afm";
254 struct stat theStatDummy;
255 if( stat( res.c_str( ), & theStatDummy ) == 0 )
257 return res;
260 throw Exceptions::MissingFontMetrics( fontName, & theFontMetricsSearchPath_ );
263 std::string
264 Lang::Font::searchCharacterEncoding( const char * encodingName )
266 std::string res;
268 if( theFontMetricsSearchPath_.empty( ) )
270 throw Exceptions::ExternalError( strrefdup( "The font metrics path was not set up. Consider defining the environment variable SHAPESFONTMETRICS." ) );
273 typedef typeof theFontMetricsSearchPath_ ListType;
274 for( ListType::const_iterator i = theFontMetricsSearchPath_.begin( ); i != theFontMetricsSearchPath_.end( ); ++i )
276 res = *i + encodingName + ".enc";
277 struct stat theStatDummy;
278 if( stat( res.c_str( ), & theStatDummy ) == 0 )
280 return res;
283 std::ostringstream msg;
284 msg << "A font operation required a character encoding file for " << encodingName << ", but it was not found. It should be named \"" << encodingName << ".enc\" and reside in a directory on the font metrics path. Please refer to your environment variable SHAPESFONTMETRICS." ;
285 throw Exceptions::MiscellaneousRequirement( strrefdup( msg ) );
289 Lang::Font::Font( const RefCountPtr< const char > fontName, RefCountPtr< SimplePDF::PDF_Object > & resource, RefCountPtr< const FontMetrics::FontMetric > metrics )
290 : fontName_( fontName ), resource_( resource ), metrics_( metrics )
292 // If this font has been instantiated before, it is reused.
294 typedef typeof theFontResourceMap_ MapType;
295 MapType::const_iterator i = theFontResourceMap_.find( fontName_ );
296 if( i != theFontResourceMap_.end( ) )
298 resource_ = i->second;
300 else
302 theFontResourceMap_.insert( MapType::value_type( fontName_, resource_ ) );
306 typedef typeof theFontMetricsMap_ MapType;
307 MapType::const_iterator i = theFontMetricsMap_.find( fontName_ );
308 if( i != theFontMetricsMap_.end( ) )
310 metrics_ = i->second;
312 else if( metrics_ != NullPtr< const FontMetrics::FontMetric >( ) )
314 theFontMetricsMap_.insert( MapType::value_type( fontName_, metrics_ ) );
319 // Note that we must not initialize name_ with builtinFontMap_[ fontConst ] here, since builtinFontMap_ may not be
320 // initialized when this constructor is invoked from globals.cc.
321 Lang::Font::Font( const RefCountPtr< const char > fontName )
322 : fontName_( fontName ), resource_( NullPtr< SimplePDF::PDF_Object >( ) ), metrics_( NullPtr< const FontMetrics::FontMetric >( ) )
325 Lang::Font::~Font( )
328 Kernel::VariableHandle
329 Lang::Font::getField( const char * fieldID, const RefCountPtr< const Lang::Value > & selfRef ) const
331 // if( strcmp( fieldID, "CIDFont?" ) == 0 )
332 // {
333 // return Helpers::newValHandle( new Lang::Boolean( metrics_->isCIDFont_ ) );
334 // }
335 if( strcmp( fieldID, "kerning?" ) == 0 )
337 return Helpers::newValHandle( new Lang::Boolean( metrics_->hasKerning_ ) );
339 if( strcmp( fieldID, "family" ) == 0 )
341 return Kernel::VariableHandle( new Kernel::Variable( this->family_name( ) ) );
343 if( strcmp( fieldID, "style" ) == 0 )
345 return Kernel::VariableHandle( new Kernel::Variable( this->style_name( ) ) );
347 if( strcmp( fieldID, "PostScript_name" ) == 0 )
349 return Kernel::VariableHandle( new Kernel::Variable( this->PostScript_name( ) ) );
352 return TypeID->getMethod( selfRef, fieldID ); /* This will throw if there is no such method. */
355 RefCountPtr< const Lang::String >
356 Lang::Font::family_name( ) const
358 return RefCountPtr< const Lang::String >( new Lang::String( fontName_ ) );
361 RefCountPtr< const Lang::String >
362 Lang::Font::style_name( ) const
364 return RefCountPtr< const Lang::String >( new Lang::String( "", true ) );
367 RefCountPtr< const Lang::Symbol >
368 Lang::Font::PostScript_name( ) const
370 return RefCountPtr< const Lang::Symbol >( new Lang::Symbol( fontName_.getPtr( ) ) );
373 RefCountPtr< const Lang::Value >
374 Lang::Font::getGlyph( Kernel::UnicodeCodePoint c, Concrete::Length size ) const
376 throw Exceptions::MiscellaneousRequirement( "It is not possible to extract individual glyphs from this font." );
380 const RefCountPtr< SimplePDF::PDF_Object > &
381 Lang::Font::resource( ) const
383 if( resource_ != NullPtr< SimplePDF::PDF_Object >( ) )
385 return resource_;
388 typedef typeof theFontResourceMap_ MapType;
391 MapType::const_iterator i = theFontResourceMap_.find( fontName_ );
392 if( i != theFontResourceMap_.end( ) )
394 resource_ = i->second;
395 return resource_;
400 RefCountPtr< SimplePDF::PDF_Dictionary > dic;
401 resource_ = SimplePDF::indirect( dic, & Kernel::theIndirectObjectCount );
402 (*dic)[ "Type" ] = SimplePDF::newName( "Font" );
403 (*dic)[ "Subtype" ] = SimplePDF::newName( "Type1" );
404 (*dic)[ "Encoding" ] = SimplePDF::newName( "MacRomanEncoding" );
405 (*dic)[ "BaseFont" ] = SimplePDF::newName( fontName_.getPtr( ) ); // Here, it is crucial that this is really a built-in font!
407 return resource_;
410 RefCountPtr< const char >
411 Lang::Font::fontName( ) const
413 return fontName_;
416 RefCountPtr< const FontMetrics::FontMetric >
417 Lang::Font::metrics( ) const
419 if( metrics_ != NullPtr< const FontMetrics::FontMetric >( ) )
421 return metrics_;
424 // Otherwise, we hope that we can find an Adobe Font Metrics file.
425 std::string filename = searchFontMetrics( fontName_ );
426 std::ifstream afmFile( filename.c_str( ) );
427 if( ! afmFile.is_open( ) )
429 std::ostringstream oss;
430 oss << "File has been located but couldn't be opened: " << filename ;
431 throw Exceptions::ExternalError( strrefdup( oss ) );
434 // I fiddle a little with the consts here...
435 FontMetrics::AFM * newMetrics = new FontMetrics::AFM;
436 metrics_ = RefCountPtr< const FontMetrics::FontMetric >( newMetrics );
438 AfmScanner scanner( newMetrics, & afmFile );
439 scanner.setTellQue( Interaction::fontMetricMessages ); // We want to know about things that are not recognized and this ignored.
440 if( Interaction::fontMetricDebug )
442 scanner.set_debug( 1 );
446 int status = scanner.yylex( );
447 if( status != 0 )
449 std::ostringstream oss;
450 oss << "Font metrics parser returned with non-zero status: " << status ;
451 throw Exceptions::InternalError( strrefdup( oss ) );
454 catch( const char * ball )
456 std::ostringstream oss;
457 oss << "Font metrics parser failed with message: " << ball ;
458 throw Exceptions::InternalError( strrefdup( oss ) );
460 catch( const RefCountPtr< const char > ball )
462 std::ostringstream oss;
463 oss << "Font metrics parser failed with message: " << ball ;
464 throw Exceptions::InternalError( strrefdup( oss ) );
467 return metrics_;
470 void
471 Lang::Font::show( std::ostream & os ) const
473 os << "< font: " << fontName_ << " >" ;
476 void
477 Lang::Font::gcMark( Kernel::GCMarkedSet & marked )
480 RefCountPtr< const Lang::Class > Lang::Font::TypeID( new Lang::SystemFinalClass( strrefdup( "Font" ), Font_register_methods ) );
481 TYPEINFOIMPL( Font );
484 Lang::PDFStandardFont::PDFStandardFont( const RefCountPtr< const char > builtInFontName )
485 : Lang::Font( builtInFontName )
488 Lang::PDFStandardFont::~PDFStandardFont( )
491 void
492 Lang::PDFStandardFont::encode( const char ** text_UTF8, size_t * in_avail, char ** dst_PDF, size_t * out_avail ) const
494 encode_MacRoman( text_UTF8, in_avail, dst_PDF, out_avail );
497 void
498 Lang::PDFStandardFont::encode( Kernel::UnicodeCodePoint c, char ** dst_PDF, size_t * out_avail ) const
500 encode_MacRoman( c, dst_PDF, out_avail );
503 void
504 Lang::PDFStandardFont::encode_MacRoman( const char ** text_UTF8, size_t * in_avail, char ** dst_PDF, size_t * out_avail )
506 iconv_t converter = Helpers::requireUTF8ToMacRomanConverter( );
507 // The ICONV_CAST macro is defined in config.h.
508 size_t count = iconv( converter,
509 ICONV_CAST( text_UTF8 ), in_avail,
510 dst_PDF, out_avail );
511 if( count == (size_t)(-1) )
513 if( errno == EILSEQ )
515 throw Exceptions::MiscellaneousRequirement( "It is suspected that one of the UFT-8 characters used in showed text cannot be represented in the MacRoman encoding (used for the 14 standard fonts of PDF; consider using a normal font instead)." );
517 else if( errno == EINVAL )
519 throw Exceptions::MiscellaneousRequirement( "It is suspected that showed text ended with an incomplete multibyte character." );
521 else if( errno == E2BIG )
523 throw Exceptions::InternalError( "The buffer allocated for UTF-8 to PDF show-text string conversion was too small." );
525 else
527 std::ostringstream msg;
528 msg << "iconv failed with an unrecognized error code: " << errno ;
529 throw Exceptions::InternalError( strrefdup( msg ) );
534 void
535 Lang::PDFStandardFont::encode_MacRoman( Kernel::UnicodeCodePoint c, char ** dst_PDF, size_t * out_avail )
537 if( *out_avail < 1 )
539 throw Exceptions::InternalError( "The buffer allocated conversion of a single UCS-4 to MacRoman was too small." );
541 **dst_PDF = c.get_MacRoman( );
542 ++*dst_PDF;
543 --*out_avail;
547 Lang::Type3Font::Type3Font( const RefCountPtr< const char > fontName, RefCountPtr< SimplePDF::PDF_Object > & resource, RefCountPtr< const FontMetrics::FontMetric > metrics )
548 : Lang::Font( fontName, resource, metrics )
551 Lang::Type3Font::~Type3Font( )
554 void
555 Lang::Type3Font::encode( const char ** text_UTF8, size_t * in_avail, char ** dst_PDF, size_t * out_avail ) const
557 Lang::PDFStandardFont::encode_MacRoman( text_UTF8, in_avail, dst_PDF, out_avail );
560 void
561 Lang::Type3Font::encode( Kernel::UnicodeCodePoint c, char ** dst_PDF, size_t * out_avail ) const
563 Lang::PDFStandardFont::encode_MacRoman( c, dst_PDF, out_avail );
568 #ifdef HAVE_FT2
570 Lang::FreeTypeFont::FreeTypeFont( FT_Face face, const RefCountPtr< const char > fontName, RefCountPtr< SimplePDF::PDF_Object > & resource, RefCountPtr< const FontMetrics::FontMetric > metrics, RefCountPtr< SimplePDF::PDF_ToUnicode::CharSet > & usedChars )
571 : Lang::Font( fontName, resource, metrics ),
572 face_( face ),
573 usedChars_( usedChars )
576 Lang::FreeTypeFont::~FreeTypeFont( )
579 void
580 Lang::FreeTypeFont::encode( const char ** text_UTF8, size_t * in_avail, char ** dst_PDF, size_t * out_avail ) const
582 iconv_t converter = Helpers::requireUTF8ToUCS4Converter( );
584 size_t bufSize = 4 * *in_avail;
585 char * buf = new char[ bufSize ];
586 char * outbuf = buf;
587 DeleteOnExit< char > bufDeleter( buf );
589 // The ICONV_CAST macro is defined in config.h.
590 size_t count = iconv( converter,
591 ICONV_CAST( text_UTF8 ), in_avail,
592 & outbuf, & bufSize );
593 if( count == (size_t)(-1) )
595 if( errno == EILSEQ )
597 throw Exceptions::MiscellaneousRequirement( "Failed to convert from UTF-8 to UCS-4 code points." );
599 else if( errno == EINVAL )
601 throw Exceptions::MiscellaneousRequirement( "It is suspected that showed text ended with an incomplete multibyte character." );
603 else if( errno == E2BIG )
605 throw Exceptions::InternalError( "The temporary UCS-4 buffer allocated for UTF-8 to PDF show-text string conversion was too small." );
607 else
609 std::ostringstream msg;
610 msg << "iconv failed with an unrecognized error code: " << errno ;
611 throw Exceptions::InternalError( strrefdup( msg ) );
614 /* The number of characters equals ( outbuf - buf ) / 4, so the memory needed to store these equals
615 * 2 * ( outbuf - buf ) / 4 = ( outbuf - buf ) / 2
617 if( static_cast< ptrdiff_t >( *out_avail ) < ( outbuf - buf ) / 2 )
619 throw Exceptions::InternalError( "The buffer allocated for UTF-8 to PDF show-text string conversion was too small." );
621 *out_avail -= ( outbuf - buf ) / 2;
622 for( const char * src = buf; src != outbuf; src += 4)
624 Kernel::UnicodeCodePoint code;
625 code.decode_UCS4( src );
626 usedChars_->insert( usedChars_->begin( ), code );
627 FT_UInt index = FT_Get_Char_Index( face_, code.get_UCS4( ) );
628 if( index == 0 )
630 std::cerr << "Warning: Missing glyph: " << code.get_UCS4( ) << std::endl ;
632 if( index > 0xFFFF )
634 throw Exceptions::InternalError( "PDF text encoding: Glyph index is out of the two byte code range." );
636 /* Remember that the endianness is unknown! Hence, don't */
637 // *reinterpret_cast< uint16_t * >( *dst_PDF ) = index;
638 // *dst_PDF += 2;
639 /* ... but do */
640 **dst_PDF = index / 0x100;
641 ++*dst_PDF;
642 **dst_PDF = index % 0x100;
643 ++*dst_PDF;
647 void
648 Lang::FreeTypeFont::encode( Kernel::UnicodeCodePoint c, char ** dst_PDF, size_t * out_avail ) const
650 if( *out_avail < 2 )
652 throw Exceptions::InternalError( "The buffer allocated conversion of one UCS-4 code point to PDF show-text encoding was too small." );
654 FT_UInt index = FT_Get_Char_Index( face_, c.get_UCS4( ) );
655 if( index == 0 )
657 std::cerr << "Warning: Missing glyph: " << c.get_UCS4( ) << std::endl ;
659 if( index > 0xFFFF )
661 throw Exceptions::InternalError( "PDF text encoding: Glyph index is out of the two byte code range." );
663 /* Remember that the endianness is unknown! Hence, don't */
664 // *reinterpret_cast< uint16_t * >( *dst_PDF ) = index;
665 // *dst_PDF += 2;
666 /* ... but do */
667 **dst_PDF = index / 0x100;
668 ++*dst_PDF;
669 **dst_PDF = index % 0x100;
670 ++*dst_PDF;
671 *out_avail -= 2;
674 RefCountPtr< const Lang::String >
675 Lang::FreeTypeFont::family_name( ) const
677 return RefCountPtr< const Lang::String >( new Lang::String( face_->family_name, true ) );
680 RefCountPtr< const Lang::String >
681 Lang::FreeTypeFont::style_name( ) const
683 return RefCountPtr< const Lang::String >( new Lang::String( face_->style_name, true ) );
686 RefCountPtr< const Lang::Symbol >
687 Lang::FreeTypeFont::PostScript_name( ) const
689 const char * PostScriptName = FT_Get_Postscript_Name( face_ );
690 if( PostScriptName == 0 )
692 throw Exceptions::MiscellaneousRequirement( "The FreeType font does not provide a PostScript name." );
694 /* It is assumed that the created symbol is not used after the face_ is destroyed! */
695 return RefCountPtr< const Lang::Symbol >( new Lang::Symbol( PostScriptName ) );
698 namespace Shapes
700 namespace Helpers
702 class GetGlyphState
704 public:
705 Lang::MultiPath2D * paths_;
706 Lang::ElementaryPath2D * contour_;
707 Concrete::PathPoint2D * last_;
708 Concrete::Length scaled_font_unit_;
710 void close( )
712 if( contour_ != 0 )
714 /* We should eliminate the duplicated point where the path begins and ends. */
715 contour_->pop_back( );
716 if( last_->rear_ != last_->mid_ )
718 contour_->front( )->rear_ = last_->rear_;
719 last_->rear_ = last_->mid_; /* This gives away the ownership of last_->rear_. */
721 delete last_;
722 last_ = 0;
723 contour_->close( );
724 contour_ = 0;
727 int move_to( const FT_Vector & to )
729 close( );
730 contour_ = new Lang::ElementaryPath2D( );
731 paths_->push_back( RefCountPtr< const Lang::Path2D >( contour_ ) );
732 last_ = new Concrete::PathPoint2D( new Concrete::Coords2D( to.x * scaled_font_unit_, to.y * scaled_font_unit_ ) );
733 contour_->push_back( last_ );
734 return 0;
736 int line_to( const FT_Vector & to )
738 last_ = new Concrete::PathPoint2D( new Concrete::Coords2D( to.x * scaled_font_unit_, to.y * scaled_font_unit_ ) );
739 contour_->push_back( last_ );
740 return 0;
742 int conic_to( const FT_Vector & control, const FT_Vector & to )
744 Concrete::Coords2D c( control.x * scaled_font_unit_, control.y * scaled_font_unit_ );
745 last_->front_ = new Concrete::Coords2D( (2./3) * c + (1./3) * *last_->mid_ );
746 last_ = new Concrete::PathPoint2D( new Concrete::Coords2D( to.x * scaled_font_unit_, to.y * scaled_font_unit_ ) );
747 last_->rear_ = new Concrete::Coords2D( (2./3) * c + (1./3) * *last_->mid_ );
748 contour_->push_back( last_ );
749 return 0;
751 int cubic_to( const FT_Vector & control1, const FT_Vector & control2, const FT_Vector & to )
753 last_->front_ = new Concrete::Coords2D( control1.x * scaled_font_unit_, control1.y * scaled_font_unit_ );
754 last_ = new Concrete::PathPoint2D( new Concrete::Coords2D( to.x * scaled_font_unit_, to.y * scaled_font_unit_ ) );
755 last_->rear_ = new Concrete::Coords2D( control2.x * scaled_font_unit_, control2.y * scaled_font_unit_ );
756 contour_->push_back( last_ );
757 return 0;
760 GetGlyphState( Lang::MultiPath2D * paths, Concrete::Length scaled_font_unit )
761 : paths_( paths ),
762 contour_( 0 ),
763 last_( 0 ),
764 scaled_font_unit_( scaled_font_unit )
766 ~GetGlyphState( )
768 close( );
771 int getGlyph_move_to( const FT_Vector * to, void * stateUntyped )
773 return reinterpret_cast< GetGlyphState * >( stateUntyped )->move_to( *to );
775 int getGlyph_line_to( const FT_Vector * to, void * stateUntyped )
777 return reinterpret_cast< GetGlyphState * >( stateUntyped )->line_to( *to );
779 int getGlyph_conic_to( const FT_Vector * control, const FT_Vector * to, void * stateUntyped )
781 return reinterpret_cast< GetGlyphState * >( stateUntyped )->conic_to( *control, *to );
783 int getGlyph_cubic_to( const FT_Vector * control1, const FT_Vector * control2, const FT_Vector * to, void * stateUntyped )
785 return reinterpret_cast< GetGlyphState * >( stateUntyped )->cubic_to( *control1, *control2, *to );
790 RefCountPtr< const Lang::Value >
791 Lang::FreeTypeFont::getGlyph( Kernel::UnicodeCodePoint c, Concrete::Length size ) const
793 FT_UInt index = FT_Get_Char_Index( face_, c.get_UCS4( ) );
794 if( index == 0 )
796 const size_t bufsize = 10;
797 char buf[ bufsize ];
798 char * tmp = buf;
799 size_t outavail = bufsize - 1;
800 c.encode_UTF8( & tmp, & outavail );
801 *tmp = '\0';
802 std::ostringstream msg;
803 msg << "The font " << fontName_ << " does not cover the character `" << buf << "´ (code point " << c.get_UCS4( ) << ")." ;
804 throw Exceptions::OutOfRange( strrefdup( msg ) );
806 FT_Error error = FT_Load_Glyph( face_, index, FT_LOAD_NO_SCALE );
807 if( error != 0 )
809 std::ostringstream msg;
810 msg << "Unable to load glyph in FreeType 2, at code point " << c.get_UCS4( ) ;
811 throw Shapes::Exceptions::OutOfRange( strrefdup( msg ) );
813 if( face_->glyph->format != FT_GLYPH_FORMAT_OUTLINE )
815 const size_t bufsize = 10;
816 char buf[ bufsize ];
817 char * tmp = buf;
818 size_t outavail = bufsize - 1;
819 c.encode_UTF8( & tmp, & outavail );
820 *tmp = '\0';
821 std::ostringstream msg;
822 msg << "Unsupported glyph format in the font " << fontName_ << ", character `" << buf << "´ (code point " << c.get_UCS4( ) << ")." ;
823 throw Exceptions::MiscellaneousRequirement( strrefdup( msg ) );
826 const FT_Outline & ol = face_->glyph->outline;
828 Helpers::FontMethod_glyph_resultFactory.set
829 ( "painter",
830 Kernel::VariableHandle( new Kernel::Variable( ( ( ol.flags & FT_OUTLINE_REVERSE_FILL ) == 0 ) ? Lang::THE_FILL : Lang::THE_FILLODD ) ) );
832 Lang::MultiPath2D * pathsPtr = new Lang::MultiPath2D( );
833 RefCountPtr< const Lang::MultiPath2D > paths( pathsPtr );
835 FT_Outline_Funcs emitters;
836 emitters.move_to = Helpers::getGlyph_move_to;
837 emitters.line_to = Helpers::getGlyph_line_to;
838 emitters.conic_to = Helpers::getGlyph_conic_to;
839 emitters.cubic_to = Helpers::getGlyph_cubic_to;
840 emitters.shift = 0;
841 emitters.delta = 0;
842 Helpers::GetGlyphState state( pathsPtr, size / static_cast< double >( face_->units_per_EM ) );
843 FT_Error error = FT_Outline_Decompose( & face_->glyph->outline, & emitters, reinterpret_cast< void * >( & state ) );
844 if( error != 0 )
846 std::ostringstream msg;
847 msg << "FreeType error, FT_Outline_Decompose returned with error: " << Kernel::FreeTypeErrorMessage( error ) ;
848 throw Exceptions::ExternalError( strrefdup( msg ) );
851 Helpers::FontMethod_glyph_resultFactory.set( "paths", Kernel::VariableHandle( new Kernel::Variable( paths ) ) );
853 return Helpers::FontMethod_glyph_resultFactory.build( );
856 #endif /* End of #ifdef HAVE_FT2. */
858 Lang::TextOperation::TextOperation( )
861 Lang::TextOperation::~TextOperation( )
864 RefCountPtr< const Lang::Class > Lang::TextOperation::TypeID( new Lang::SystemFinalClass( strrefdup( "TextOperation" ) ) );
865 TYPEINFOIMPL( TextOperation );
868 Lang::KernedText::KernedText( const RefCountPtr< const Kernel::TextState > & textState, const RefCountPtr< const Kernel::GraphicsState > & metaState )
869 : textState_( textState ), metaState_( metaState ), maxLength_( 0 )
872 Lang::KernedText::KernedText( const RefCountPtr< const Kernel::TextState > & textState, const RefCountPtr< const Kernel::GraphicsState > & metaState, const RefCountPtr< const Lang::String > & str)
873 : textState_( textState ), metaState_( metaState ), maxLength_( 0 )
875 pushString( str );
878 Lang::KernedText::~KernedText( )
881 Kernel::VariableHandle
882 Lang::KernedText::getField( const char * fieldID, const RefCountPtr< const Lang::Value > & selfRef ) const
884 if( strcmp( fieldID, "list" ) == 0 )
886 return Kernel::VariableHandle( new Kernel::Variable( makeList( ) ) );
888 throw Exceptions::NonExistentMember( getTypeName( ), fieldID );
891 RefCountPtr< const Lang::SingleList >
892 Lang::KernedText::makeList( ) const
894 /* The list is first computed in a bidirectional list, and then we construct the stateless cons list.
897 /* I'm lazy today, so i use a cons pair to group the horizontal step with the glyph. The better solution
898 * would be to use a structure with nicely named fields...
901 std::list< RefCountPtr< const Lang::Value > > revlist;
903 iconv_t converter = Helpers::requireUTF8ToUCS4Converter( );
905 RefCountPtr< FontMetrics::WritingDirectionMetrics > horizontalMetrics = textState_->font_->metrics( )->horizontalMetrics_;
906 if( horizontalMetrics == NullPtr< FontMetrics::WritingDirectionMetrics >( ) )
908 throw Exceptions::FontMetricsError( textState_->font_->fontName( ), strrefdup( "No horizontal metrics defined." ) );
910 const FontMetrics::CharacterMetrics * defaultCharMetrics = horizontalMetrics->default_char( );
912 Concrete::Length ySize = textState_->size_;
913 Concrete::Length xSize = ySize * textState_->horizontalScaling_;
914 Concrete::Length characterTrackKern = textState_->horizontalScaling_ * textState_->characterSpacing_;
915 Concrete::Length wordTrackKern = textState_->horizontalScaling_ * textState_->wordSpacing_;
917 Concrete::Length xpos = 0;
919 size_t bufSize = 4 * maxLength_; // This will be enough if UCS4 coding is used, since this encoding uses only one byte per character.
920 char * buf = new char[ bufSize ];
921 DeleteOnExit< char > bufDeleter( buf );
923 FontMetrics::CharacterMetrics tmpCharMetric( 0 );
925 typedef typeof strings_ ListType;
926 std::list< double >::const_iterator ki = kernings_.begin( );
927 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
929 if( *i != NullPtr< const Lang::String >( ) )
931 const char * inbuf = (*i)->val_.getPtr( );
932 if( strchr( inbuf, '\n' ) != 0 )
934 throw Exceptions::MiscellaneousRequirement( "Newlines cannot be represented in the pos-character list." );
936 char * outbuf = buf;
937 size_t inbytesleft = strlen( inbuf );
938 size_t outbytesleft = bufSize;
939 // The ICONV_CAST macro is defined in config.h.
940 size_t count = iconv( converter,
941 ICONV_CAST( & inbuf ), & inbytesleft,
942 & outbuf, & outbytesleft );
943 if( count == (size_t)(-1) )
945 if( errno == EILSEQ )
947 throw Exceptions::MiscellaneousRequirement( "It is suspected that string data is not proper UFT-8, since conversion to UCS-4 failed." );
949 else if( errno == EINVAL )
951 throw Exceptions::MiscellaneousRequirement( "It is suspected that showed text ended with an incomplete multibyte character." );
953 else if( errno == E2BIG )
955 throw Exceptions::InternalError( "The buffer allocated for UTF-8 to UCS-4 conversion was too small." );
957 else
959 std::ostringstream msg;
960 msg << "iconv failed with an unrecognized error code: " << errno ;
961 throw Exceptions::InternalError( strrefdup( msg ) );
964 for( const char * src = buf; src != outbuf; src += 4 )
966 Kernel::UnicodeCodePoint code;
967 code.decode_UCS4( src );
968 if( code == Kernel::UnicodeCodePoint::SPACE ) /* test for space */
970 // Observe textState_->wordSpacing_
971 const FontMetrics::CharacterMetrics * charMetrics = horizontalMetrics->charByCode( code, & tmpCharMetric );
972 xpos += xSize * charMetrics->horizontalCharWidthX_;
973 xpos += characterTrackKern;
974 xpos += wordTrackKern;
976 else /* there should not be any newlines in the string, so we expect this to be a non-white character */
978 // Observe textState_->characterSpacing_
979 const FontMetrics::CharacterMetrics * charMetrics = horizontalMetrics->charByCode( code, & tmpCharMetric );
980 if( Computation::fontMetricGuessIsError && charMetrics == defaultCharMetrics )
982 std::ostringstream msg;
983 msg << "Character at offset " << src - buf << " in \"" << buf << "\" was not found in font metrics (and according to your options, guessing is not OK)." ;
984 throw Exceptions::FontMetricsError( textState_->font_->fontName( ), strrefdup( msg ) );
986 revlist.push_back( RefCountPtr< const Lang::Value >
987 ( new Lang::ConsPair
988 ( Helpers::newValHandle( new Lang::Length( xpos ) ),
989 Helpers::newValHandle( new Lang::KernedText( textState_, metaState_, oneUCS4ToUTF8( src ) ) ) ) ) );
990 /* Although not as efficient, it becomes easier to use the list if the distance is
991 * measured from the start of the text rather than from the previous character.
993 // xpos = 0;
994 xpos += xSize * charMetrics->horizontalCharWidthX_;
995 xpos += characterTrackKern;
999 else
1001 if( ki == kernings_.end( ) )
1003 throw Exceptions::InternalError( "Short of kerning values in KernedText::measure." );
1005 xpos -= xSize * *ki;
1006 ++ki;
1009 if( ki != kernings_.end( ) )
1011 throw Exceptions::InternalError( "Too many kerning values in KernedText::writePDFVectorTo." );
1014 RefCountPtr< const Lang::SingleList > res = Lang::THE_CONS_NULL;
1015 while( ! revlist.empty( ) )
1017 res = RefCountPtr< const Lang::SingleList >( new Lang::SingleListPair( Kernel::VariableHandle( new Kernel::Variable( revlist.back( ) ) ),
1018 res ) );
1019 revlist.pop_back( );
1021 return res;
1024 RefCountPtr< const Lang::String >
1025 Lang::KernedText::oneUCS4ToUTF8( const char * c )
1027 iconv_t converter = Helpers::requireUCS4ToUTF8Converter( );
1029 const size_t BUF_SIZE = 9;
1030 char buf[ BUF_SIZE ];
1032 char charbuf[ 5 ];
1033 charbuf[0] = *c;
1034 ++c;
1035 charbuf[1] = *c;
1036 ++c;
1037 charbuf[2] = *c;
1038 ++c;
1039 charbuf[3] = *c;
1040 charbuf[4] = '\0';
1042 char * outbuf = buf;
1043 const char * inbuf = charbuf;
1044 size_t inbytesleft = 4;
1045 size_t outbytesleft = BUF_SIZE - 1;
1046 // The ICONV_CAST macro is defined in config.h.
1047 size_t count = iconv( converter,
1048 ICONV_CAST( & inbuf ), & inbytesleft,
1049 & outbuf, & outbytesleft );
1050 if( count == (size_t)(-1) )
1052 throw Exceptions::ExternalError( "Conversion of one UCS-4 character to UTF-8 failed." );
1054 *outbuf = '\0';
1055 return RefCountPtr< const Lang::String >( new Lang::String( strrefdup( buf ) ) );
1058 void
1059 Lang::KernedText::pushString( const RefCountPtr< const Lang::String > & str )
1061 strings_.push_back( str );
1062 maxLength_ = std::max( maxLength_, strlen( str->val_.getPtr( ) ) );
1065 void
1066 Lang::KernedText::pushKerning( double kerning )
1068 strings_.push_back( NullPtr< const Lang::String >( ) );
1069 kernings_.push_back( kerning );
1072 void
1073 Lang::KernedText::show( std::ostream & os ) const
1075 typedef typeof strings_ ListType;
1076 std::list< double >::const_iterator ki = kernings_.begin( );
1077 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
1079 if( *i != NullPtr< const Lang::String >( ) )
1081 os << "(" << (*i)->val_.getPtr( ) << ")" ;
1083 else
1085 if( ki == kernings_.end( ) )
1087 throw Exceptions::InternalError( "Short of kerning values in KernedText::show." );
1089 os << *ki ;
1090 ++ki;
1093 if( ki != kernings_.end( ) )
1095 throw Exceptions::InternalError( "Too many kerning values in KernedText::show." );
1100 void
1101 Lang::KernedText::shipout( std::ostream & os, Kernel::PageContentStates * pdfState, const Lang::Transform2D & tf, bool clip ) const
1103 pdfState->text_.synchAssertKnockout( os, textState_.getPtr( ), pdfState->resources_.getPtr( ), clip );
1104 switch( textState_->mode_ )
1106 case Lang::TextRenderingMode::FILL:
1107 case Lang::TextRenderingMode::FILLCLIP:
1108 pdfState->graphics_.synchForNonStroke( os, metaState_.getPtr( ), pdfState->resources_.getPtr( ) );
1109 break;
1110 case Lang::TextRenderingMode::STROKE:
1111 case Lang::TextRenderingMode::STROKECLIP:
1112 pdfState->graphics_.synchForStroke( os, metaState_.getPtr( ), pdfState->resources_.getPtr( ) );
1113 break;
1114 case Lang::TextRenderingMode::FILLSTROKE:
1115 case Lang::TextRenderingMode::FILLSTROKECLIP:
1116 pdfState->graphics_.synchForStroke( os, metaState_.getPtr( ), pdfState->resources_.getPtr( ) );
1117 pdfState->graphics_.synchForNonStroke( os, metaState_.getPtr( ), pdfState->resources_.getPtr( ) );
1118 break;
1119 case Lang::TextRenderingMode::INVISIBLE:
1120 case Lang::TextRenderingMode::CLIP:
1121 break;
1122 case Lang::TextRenderingMode::UNDEFINED:
1123 pdfState->graphics_.synchForStroke( os, metaState_.getPtr( ), pdfState->resources_.getPtr( ) );
1124 pdfState->graphics_.synchForNonStroke( os, metaState_.getPtr( ), pdfState->resources_.getPtr( ) );
1125 default:
1126 throw Exceptions::InternalError( "KernedText::writePDFVectorTo: Text rendering mode out of range." );
1130 size_t bufSize = 4 * maxLength_; // Even with UTF16BE encoding, no character should use more than 4 bytes.
1131 // The extra one byte will be used to terminate the string.
1132 char * buf = new char[ bufSize ];
1133 DeleteOnExit< char > bufDeleter( buf );
1135 typedef typeof strings_ ListType;
1136 std::list< double >::const_iterator ki = kernings_.begin( );
1137 os << "[ " ;
1138 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
1140 if( *i != NullPtr< const Lang::String >( ) )
1142 os << "(" ;
1143 const char * line = (*i)->val_.getPtr( );
1144 while( true )
1146 const char * nextLine = strchr( line, '\n' ); /* Search for line breaks before we convert from UTF8. */
1147 const char * inbuf = line;
1148 char * outbuf = buf;
1149 size_t inbytesleft = ( nextLine != 0 ) ? ( nextLine - line ) : strlen( line );
1150 size_t outbytesleft = bufSize;
1151 textState_->font_->encode( & inbuf, & inbytesleft,
1152 & outbuf, & outbytesleft );
1153 const char * end = buf + ( bufSize - outbytesleft );
1154 for( const char * src = buf; src != end; ++src )
1156 switch( *src )
1158 case '(':
1159 os << "\\(" ;
1160 break;
1161 case ')':
1162 os << "\\)" ;
1163 break;
1164 case '\\':
1165 os << "\\\\" ;
1166 break;
1167 default:
1168 os << *src ;
1171 if( nextLine == 0 )
1173 break;
1175 os << ")] TJ T* [(" ;
1176 line = nextLine;
1177 ++line; /* Skip the \n character */
1179 os << ") " ;
1181 else
1183 if( ki == kernings_.end( ) )
1185 throw Exceptions::InternalError( "Short of kerning values in KernedText::writePDFVectorTo." );
1187 os << 1000 * *ki << " " ;
1188 ++ki;
1191 if( ki != kernings_.end( ) )
1193 throw Exceptions::InternalError( "Too many kerning values in KernedText::writePDFVectorTo." );
1196 os << "] TJ " << std::endl ;
1199 void
1200 Lang::KernedText::measure( Lang::Transform2D * textMatrix, Lang::Transform2D * textLineMatrix, Concrete::Length * xmin, Concrete::Length * ymin, Concrete::Length * xmax, Concrete::Length * ymax ) const
1202 iconv_t converter = Helpers::requireUTF8ToUCS4Converter( );
1204 RefCountPtr< FontMetrics::WritingDirectionMetrics > horizontalMetrics = textState_->font_->metrics( )->horizontalMetrics_;
1205 if( horizontalMetrics == NullPtr< FontMetrics::WritingDirectionMetrics >( ) )
1207 throw Exceptions::FontMetricsError( textState_->font_->fontName( ), strrefdup( "No horizontal metrics defined." ) );
1209 const FontMetrics::CharacterMetrics * defaultCharMetrics = horizontalMetrics->default_char( );
1211 Concrete::Length ySize = textState_->size_;
1212 Concrete::Length xSize = ySize * textState_->horizontalScaling_;
1213 Concrete::Length characterTrackKern = textState_->horizontalScaling_ * textState_->characterSpacing_;
1214 Concrete::Length wordTrackKern = textState_->horizontalScaling_ * textState_->wordSpacing_;
1216 size_t bufSize = 4 * maxLength_;
1217 char * buf = new char[ bufSize ];
1218 DeleteOnExit< char > bufDeleter( buf );
1220 FontMetrics::CharacterMetrics tmpCharMetric( 0 );
1222 if( textLineMatrix->isTranslation( ) )
1224 Concrete::Length x0 = textLineMatrix->xt_;
1225 Concrete::Length y0 = textLineMatrix->yt_ + textState_->riseConcrete( );
1226 Concrete::Length xpos = 0;
1228 typedef typeof strings_ ListType;
1229 std::list< double >::const_iterator ki = kernings_.begin( );
1230 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
1232 if( *i != NullPtr< const Lang::String >( ) )
1234 const char * inbuf = (*i)->val_.getPtr( );
1235 char * outbuf = buf;
1236 size_t inbytesleft = strlen( inbuf );
1237 size_t outbytesleft = bufSize;
1238 // The ICONV_CAST macro is defined in config.h.
1239 size_t count = iconv( converter,
1240 ICONV_CAST( & inbuf ), & inbytesleft,
1241 & outbuf, & outbytesleft );
1242 if( count == (size_t)(-1) )
1244 if( errno == EILSEQ )
1246 throw Exceptions::MiscellaneousRequirement( "Failed to convert from UFT-8 to UCS-4 code points." );
1248 else if( errno == EINVAL )
1250 throw Exceptions::MiscellaneousRequirement( "It is suspected that showed text ended with an incomplete multibyte character." );
1252 else if( errno == E2BIG )
1254 throw Exceptions::InternalError( "The buffer allocated for UTF-8 to UCS-4 conversion was too small." );
1256 else
1258 std::ostringstream msg;
1259 msg << "iconv failed with an unrecognized error code: " << errno ;
1260 throw Exceptions::InternalError( strrefdup( msg ) );
1263 for( const char * src = buf; src != outbuf; src += 4 )
1265 Kernel::UnicodeCodePoint code;
1266 code.decode_UCS4( src );
1267 if( code == Kernel::UnicodeCodePoint::SPACE )
1269 // Observe textState_->wordSpacing_
1270 // In addition, it seems reasonable to not let the space character affect the bounding box.
1271 const FontMetrics::CharacterMetrics * charMetrics = horizontalMetrics->charByCode( code, & tmpCharMetric );
1272 xpos += xSize * charMetrics->horizontalCharWidthX_;
1273 xpos += characterTrackKern;
1274 xpos += wordTrackKern;
1276 else if( code == Kernel::UnicodeCodePoint::NEWLINE )
1278 textMatrix->prependShift( Concrete::Coords2D( 0, - textState_->leadingConcrete( ) ) );
1279 textLineMatrix->replaceBy( *textMatrix );
1280 x0 = textLineMatrix->xt_;
1281 y0 = textLineMatrix->yt_ + textState_->riseConcrete( );
1282 xpos = 0;
1284 else
1286 // Observe textState_->characterSpacing_
1287 const FontMetrics::CharacterMetrics * charMetrics = horizontalMetrics->charByCode( code, & tmpCharMetric );
1288 if( Computation::fontMetricGuessIsError && charMetrics == defaultCharMetrics )
1290 std::ostringstream msg;
1291 msg << "Character at offset " << src - buf << " in \"" << buf << "\" was not found in font metrics (and according to your options, guessing is not OK)." ;
1292 throw Exceptions::FontMetricsError( textState_->font_->fontName( ), strrefdup( msg ) );
1294 *xmin = std::min( *xmin, x0 + xpos + xSize * charMetrics->xmin_ );
1295 *ymin = std::min( *ymin, y0 + ySize * charMetrics->ymin_ );
1296 *xmax = std::max( *xmax, x0 + xpos + xSize * charMetrics->xmax_ );
1297 *ymax = std::max( *ymax, y0 + ySize * charMetrics->ymax_ );
1298 xpos += xSize * charMetrics->horizontalCharWidthX_;
1299 xpos += characterTrackKern;
1303 else
1305 if( ki == kernings_.end( ) )
1307 throw Exceptions::InternalError( "Short of kerning values in KernedText::measure." );
1309 xpos -= xSize * *ki;
1310 ++ki;
1313 if( ki != kernings_.end( ) )
1315 throw Exceptions::InternalError( "Too many kerning values in KernedText::writePDFVectorTo." );
1318 textLineMatrix->prependXShift( xpos );
1320 else
1322 Concrete::Length rise = textState_->riseConcrete( );
1323 // std::cerr << "@<< @stroking:RGB_RED | [stroke [shift " << Helpers::shapesFormat( Concrete::Coords2D( 0, 0 ).transformed( *textLineMatrix ) ) << "] [] [circle 2bp]]" << std::endl ;
1325 typedef typeof strings_ ListType;
1326 std::list< double >::const_iterator ki = kernings_.begin( );
1327 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
1329 if( *i != NullPtr< const Lang::String >( ) )
1331 const char * inbuf = (*i)->val_.getPtr( );
1332 char * outbuf = buf;
1333 size_t inbytesleft = strlen( inbuf );
1334 size_t outbytesleft = bufSize;
1335 // The ICONV_CAST macro is defined in config.h.
1336 size_t count = iconv( converter,
1337 ICONV_CAST( & inbuf ), & inbytesleft,
1338 & outbuf, & outbytesleft );
1339 if( count == (size_t)(-1) )
1341 if( errno == EILSEQ )
1343 throw Exceptions::MiscellaneousRequirement( "It is suspected that one of the UFT-8 characters used in showed text has no MacRoman representation." );
1345 else if( errno == EINVAL )
1347 throw Exceptions::MiscellaneousRequirement( "It is suspected that showed text ended with an incomplete multibyte character." );
1349 else if( errno == E2BIG )
1351 throw Exceptions::InternalError( "The buffer allocated for UTF-8 to UCS-4 conversion was too small." );
1353 else
1355 std::ostringstream msg;
1356 msg << "iconv failed with an unrecognized error code: " << errno ;
1357 throw Exceptions::InternalError( strrefdup( msg ) );
1360 for( const char * src = buf; src != outbuf; src += 4 )
1362 Kernel::UnicodeCodePoint code;
1363 code.decode_UCS4( src );
1364 if( code == Kernel::UnicodeCodePoint::SPACE )
1366 // Observe textState_->wordSpacing_
1367 // In addition, it seems reasonable to not let the space character affect the bounding box.
1368 const FontMetrics::CharacterMetrics * charMetrics = horizontalMetrics->charByCode( code, & tmpCharMetric );
1369 textLineMatrix->prependXShift( xSize * charMetrics->horizontalCharWidthX_ + characterTrackKern + wordTrackKern );
1370 // std::cerr << "@<< @stroking:RGB_BLUE | [stroke [shift " << Helpers::shapesFormat( Concrete::Coords2D( 0, 0 ).transformed( *textLineMatrix ) ) << "] [] [circle 2bp]]" << std::endl ;
1372 else if( code == Kernel::UnicodeCodePoint::NEWLINE )
1374 textMatrix->prependShift( Concrete::Coords2D( 0, - textState_->leadingConcrete( ) ) );
1375 textLineMatrix->replaceBy( *textMatrix );
1376 // std::cerr << "@<< @stroking:RGB_RED | [stroke [shift " << Helpers::shapesFormat( Concrete::Coords2D( 0, 0 ).transformed( *textLineMatrix ) ) << "] [] [circle 2bp]]" << std::endl ;
1378 else
1380 // Observe textState_->characterSpacing_
1381 const FontMetrics::CharacterMetrics * charMetrics = horizontalMetrics->charByCode( code, & tmpCharMetric );
1382 if( Computation::fontMetricGuessIsError && charMetrics == defaultCharMetrics )
1384 std::ostringstream msg;
1385 msg << "Character at offset " << src - buf << " in \"" << buf << "\" was not found in the font metrics (and according to your options, guessing is not OK)." ;
1386 throw Exceptions::FontMetricsError( textState_->font_->fontName( ), strrefdup( msg ) );
1389 Concrete::Coords2D x0y0 = Concrete::Coords2D( xSize * charMetrics->xmin_, ySize * charMetrics->ymin_ + rise ).transformed( *textLineMatrix );
1390 Concrete::Coords2D x0y1 = Concrete::Coords2D( xSize * charMetrics->xmin_, ySize * charMetrics->ymax_ + rise ).transformed( *textLineMatrix );
1391 Concrete::Coords2D x1y0 = Concrete::Coords2D( xSize * charMetrics->xmax_, ySize * charMetrics->ymin_ + rise ).transformed( *textLineMatrix );
1392 Concrete::Coords2D x1y1 = Concrete::Coords2D( xSize * charMetrics->xmax_, ySize * charMetrics->ymax_ + rise ).transformed( *textLineMatrix );
1393 // std::cerr << "@<< @width:0.3bp | [stroke "<< Helpers::shapesFormat( x0y0 ) << "--" << Helpers::shapesFormat( x0y1 ) << "--" << Helpers::shapesFormat( x1y1 ) << "--" << Helpers::shapesFormat( x1y0 ) << "--cycle]" << std::endl ;
1395 *xmin = std::min( *xmin, x0y0.x_ );
1396 *ymin = std::min( *ymin, x0y0.y_ );
1397 *xmax = std::max( *xmax, x0y0.x_ );
1398 *ymax = std::max( *ymax, x0y0.y_ );
1400 *xmin = std::min( *xmin, x0y1.x_ );
1401 *ymin = std::min( *ymin, x0y1.y_ );
1402 *xmax = std::max( *xmax, x0y1.x_ );
1403 *ymax = std::max( *ymax, x0y1.y_ );
1405 *xmin = std::min( *xmin, x1y0.x_ );
1406 *ymin = std::min( *ymin, x1y0.y_ );
1407 *xmax = std::max( *xmax, x1y0.x_ );
1408 *ymax = std::max( *ymax, x1y0.y_ );
1410 *xmin = std::min( *xmin, x1y1.x_ );
1411 *ymin = std::min( *ymin, x1y1.y_ );
1412 *xmax = std::max( *xmax, x1y1.x_ );
1413 *ymax = std::max( *ymax, x1y1.y_ );
1415 textLineMatrix->prependXShift( xSize * charMetrics->horizontalCharWidthX_ + characterTrackKern );
1416 // std::cerr << "@<< [stroke [shift " << Helpers::shapesFormat( Concrete::Coords2D( 0, 0 ).transformed( *textLineMatrix ) ) << "] [] [circle 2bp]]" << std::endl ;
1420 else
1422 if( ki == kernings_.end( ) )
1424 throw Exceptions::InternalError( "Short of kerning values in KernedText::measure." );
1426 textLineMatrix->prependXShift( - xSize * *ki );
1427 ++ki;
1430 if( ki != kernings_.end( ) )
1432 throw Exceptions::InternalError( "Too many kerning values in KernedText::writePDFVectorTo." );
1437 void
1438 Lang::KernedText::push( Lang::KernedText * dst ) const
1440 typedef typeof strings_ ListType;
1441 std::list< double >::const_iterator ki = kernings_.begin( );
1442 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
1444 if( *i != NullPtr< const Lang::String >( ) )
1446 dst->pushString( *i );
1448 else
1450 dst->pushKerning( *ki );
1451 ++ki;
1457 void
1458 Lang::KernedText::gcMark( Kernel::GCMarkedSet & marked )
1460 typedef typeof strings_ ListType;
1461 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
1463 if( *i != NullPtr< const Lang::String >( ) )
1465 const_cast< Lang::String * >( i->getPtr( ) )->gcMark( marked );
1470 Lang::TextNewline::TextNewline( const Concrete::Length tx, const Concrete::Length ty )
1471 : t_( tx, ty )
1474 Lang::TextNewline::~TextNewline( )
1477 void
1478 Lang::TextNewline::show( std::ostream & os ) const
1480 os << "Newline with offset in bp: " << t_ ;
1483 void
1484 Lang::TextNewline::shipout( std::ostream & os, Kernel::PageContentStates * pdfState, const Lang::Transform2D & tf, bool clip ) const
1486 os << t_ << " Td " ;
1489 void
1490 Lang::TextNewline::measure( Lang::Transform2D * textMatrix, Lang::Transform2D * textLineMatrix, Concrete::Length * xmin, Concrete::Length * ymin, Concrete::Length * xmax, Concrete::Length * ymax ) const
1492 textMatrix->prependShift( t_ );
1493 textLineMatrix->replaceBy( *textMatrix );
1496 void
1497 Lang::TextNewline::gcMark( Kernel::GCMarkedSet & marked )
1500 Lang::TextMoveto::TextMoveto( const RefCountPtr< const Lang::Transform2D > & tf )
1501 : tf_( tf )
1504 Lang::TextMoveto::~TextMoveto( )
1507 void
1508 Lang::TextMoveto::show( std::ostream & os ) const
1510 os << "Moveto command" ;
1513 void
1514 Lang::TextMoveto::shipout( std::ostream & os, Kernel::PageContentStates * pdfState, const Lang::Transform2D & tf, bool clip ) const
1516 Lang::Transform2D( tf, *tf_ ).shipout( os );
1517 os << " Tm" << std::endl ;
1520 void
1521 Lang::TextMoveto::measure( Lang::Transform2D * textMatrix, Lang::Transform2D * textLineMatrix, Concrete::Length * xmin, Concrete::Length * ymin, Concrete::Length * xmax, Concrete::Length * ymax ) const
1523 textMatrix->replaceBy( *tf_ );
1524 textLineMatrix->replaceBy( *textMatrix );
1527 void
1528 Lang::TextMoveto::gcMark( Kernel::GCMarkedSet & marked )
1530 const_cast< Lang::Transform2D * >( tf_.getPtr( ) )->gcMark( marked );
1535 typedef enum { FILL = 0, STROKE, FILLSTROKE, INVISIBLE, FILLCLIP, STROKECLIP, FILLSTROKECLIP, CLIP, UNDEFINED } ValueType;
1536 ValueType mode_;
1538 Lang::TextRenderingMode::TextRenderingMode( const ValueType & mode )
1539 : mode_( mode )
1542 Lang::TextRenderingMode::TextRenderingMode( bool fill, bool stroke, bool clip )
1544 switch( ( fill ? 0x01 : 0x00 ) +
1545 ( stroke ? 0x02 : 0x00 ) +
1546 ( clip ? 0x04 : 0x00 ) )
1548 case 0x00:
1549 mode_ = INVISIBLE;
1550 break;
1551 case 0x01:
1552 mode_ = FILL;
1553 break;
1554 case 0x02:
1555 mode_ = STROKE;
1556 break;
1557 case 0x03:
1558 mode_ = FILLSTROKE;
1559 break;
1560 case 0x04:
1561 mode_ = CLIP;
1562 break;
1563 case 0x05:
1564 mode_ = FILLCLIP;
1565 break;
1566 case 0x06:
1567 mode_ = STROKECLIP;
1568 break;
1569 case 0x07:
1570 mode_ = FILLSTROKECLIP;
1571 break;
1572 default:
1573 throw Exceptions::InternalError( "Semi-static switch out of range in TextRenderingMode::TextRenderingMode." );
1577 Lang::TextRenderingMode::ValueType
1578 Lang::TextRenderingMode::clipMode( ValueType mode )
1580 switch( mode )
1582 case INVISIBLE:
1583 return CLIP;
1584 case FILL:
1585 return FILLCLIP;
1586 case STROKE:
1587 return STROKECLIP;
1588 case FILLSTROKE:
1589 return FILLSTROKECLIP;
1590 default:
1591 return UNDEFINED;
1595 Lang::TextRenderingMode::~TextRenderingMode( )
1598 RefCountPtr< const Lang::Class > Lang::TextRenderingMode::TypeID( new Lang::SystemFinalClass( strrefdup( "TextRenderingMode" ) ) );
1599 TYPEINFOIMPL( TextRenderingMode );
1602 Lang::CharacterSpacingBinding::CharacterSpacingBinding( const char * id, const Ast::DynamicBindingExpression * bindingExpr, const Concrete::Length spacing )
1603 : bindingExpr_( bindingExpr ), spacing_( spacing ), isRelative_( false ), id_( id )
1606 Lang::CharacterSpacingBinding::CharacterSpacingBinding( const char * id, const Ast::DynamicBindingExpression * bindingExpr, const double r )
1607 : bindingExpr_( bindingExpr ), spacing_( r ), isRelative_( true ), id_( id )
1610 Lang::CharacterSpacingBinding::~CharacterSpacingBinding( )
1613 void
1614 Lang::CharacterSpacingBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
1616 if( *sysBindings == 0 )
1618 *sysBindings = new Kernel::SystemDynamicVariables( );
1619 Kernel::TextState * newState = new Kernel::TextState( );
1620 if( isRelative_ )
1622 newState->setCharacterSpacing( Concrete::Length::offtype( spacing_ ) );
1624 else
1626 newState->setCharacterSpacing( spacing_ );
1628 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1629 return;
1632 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
1634 Kernel::TextState * newState = new Kernel::TextState( );
1635 if( isRelative_ )
1637 newState->setCharacterSpacing( Concrete::Length::offtype( spacing_ ) );
1639 else
1641 newState->setCharacterSpacing( spacing_ );
1643 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1644 return;
1647 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
1649 if( newState->hasCharacterSpacing( ) )
1651 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
1654 if( isRelative_ )
1656 newState->setCharacterSpacing( Concrete::Length::offtype( spacing_ ) );
1658 else
1660 newState->setCharacterSpacing( spacing_ );
1662 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1665 void
1666 Lang::CharacterSpacingBinding::show( std::ostream & os ) const
1668 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":" ;
1669 if( isRelative_ )
1671 os << Concrete::Length::offtype( spacing_ ) ;
1673 else
1675 os << spacing_ / Interaction::displayUnit << Interaction::displayUnitName ;
1679 void
1680 Lang::CharacterSpacingBinding::gcMark( Kernel::GCMarkedSet & marked )
1684 Lang::WordSpacingBinding::WordSpacingBinding( const char * id, const Ast::DynamicBindingExpression * bindingExpr, const Concrete::Length spacing )
1685 : bindingExpr_( bindingExpr ), spacing_( spacing ), isRelative_( false ), id_( id )
1688 Lang::WordSpacingBinding::WordSpacingBinding( const char * id, const Ast::DynamicBindingExpression * bindingExpr, const double r )
1689 : bindingExpr_( bindingExpr ), spacing_( r ), isRelative_( true ), id_( id )
1692 Lang::WordSpacingBinding::~WordSpacingBinding( )
1695 void
1696 Lang::WordSpacingBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
1698 if( *sysBindings == 0 )
1700 *sysBindings = new Kernel::SystemDynamicVariables( );
1701 Kernel::TextState * newState = new Kernel::TextState( );
1702 if( isRelative_ )
1704 newState->setWordSpacing( Concrete::Length::offtype( spacing_ ) );
1706 else
1708 newState->setWordSpacing( spacing_ );
1710 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1711 return;
1714 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
1716 Kernel::TextState * newState = new Kernel::TextState( );
1717 if( isRelative_ )
1719 newState->setWordSpacing( Concrete::Length::offtype( spacing_ ) );
1721 else
1723 newState->setWordSpacing( spacing_ );
1725 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1726 return;
1729 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
1731 if( newState->hasWordSpacing( ) )
1733 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
1736 if( isRelative_ )
1738 newState->setWordSpacing( Concrete::Length::offtype( spacing_ ) );
1740 else
1742 newState->setWordSpacing( spacing_ );
1744 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1747 void
1748 Lang::WordSpacingBinding::show( std::ostream & os ) const
1750 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":" ;
1751 if( isRelative_ )
1753 os << Concrete::Length::offtype( spacing_ ) ;
1755 else
1757 os << spacing_ / Interaction::displayUnit << Interaction::displayUnitName ;
1761 void
1762 Lang::WordSpacingBinding::gcMark( Kernel::GCMarkedSet & marked )
1766 Lang::HorizontalScalingBinding::HorizontalScalingBinding( const char * id, const Ast::DynamicBindingExpression * bindingExpr, double scaling )
1767 : bindingExpr_( bindingExpr ), scaling_( scaling ), id_( id )
1770 Lang::HorizontalScalingBinding::~HorizontalScalingBinding( )
1773 void
1774 Lang::HorizontalScalingBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
1776 if( *sysBindings == 0 )
1778 *sysBindings = new Kernel::SystemDynamicVariables( );
1779 Kernel::TextState * newState = new Kernel::TextState( );
1780 newState->horizontalScaling_ = scaling_;
1781 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1782 return;
1785 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
1787 Kernel::TextState * newState = new Kernel::TextState( );
1788 newState->horizontalScaling_ = scaling_;
1789 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1790 return;
1793 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
1795 if( ! IS_NAN( newState->horizontalScaling_ ) )
1797 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
1800 newState->horizontalScaling_ = scaling_;
1801 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1804 void
1805 Lang::HorizontalScalingBinding::show( std::ostream & os ) const
1807 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":"
1808 << scaling_ ;
1811 void
1812 Lang::HorizontalScalingBinding::gcMark( Kernel::GCMarkedSet & marked )
1816 Lang::LeadingBinding::LeadingBinding( const char * id, const Ast::DynamicBindingExpression * bindingExpr, const Concrete::Length ty )
1817 : bindingExpr_( bindingExpr ), ty_( ty ), isRelative_( false ), id_( id )
1820 Lang::LeadingBinding::LeadingBinding( const char * id, const Ast::DynamicBindingExpression * bindingExpr, const double r )
1821 : bindingExpr_( bindingExpr ), ty_( r ), isRelative_( true ), id_( id )
1824 Lang::LeadingBinding::~LeadingBinding( )
1827 void
1828 Lang::LeadingBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
1830 if( *sysBindings == 0 )
1832 *sysBindings = new Kernel::SystemDynamicVariables( );
1833 Kernel::TextState * newState = new Kernel::TextState( );
1834 if( isRelative_ )
1836 newState->setLeading( Concrete::Length::offtype( ty_ ) );
1838 else
1840 newState->setLeading( ty_ );
1842 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1843 return;
1846 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
1848 Kernel::TextState * newState = new Kernel::TextState( );
1849 if( isRelative_ )
1851 newState->setLeading( Concrete::Length::offtype( ty_ ) );
1853 else
1855 newState->setLeading( ty_ );
1857 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1858 return;
1861 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
1863 if( newState->hasLeading( ) )
1865 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
1868 if( isRelative_ )
1870 newState->setLeading( Concrete::Length::offtype( ty_ ) );
1872 else
1874 newState->setLeading( ty_ );
1876 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1879 void
1880 Lang::LeadingBinding::show( std::ostream & os ) const
1882 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":" ;
1883 if( isRelative_ )
1885 os << Concrete::Length::offtype( ty_ ) ;
1887 else
1889 os << ty_ / Interaction::displayUnit << Interaction::displayUnitName ;
1893 void
1894 Lang::LeadingBinding::gcMark( Kernel::GCMarkedSet & marked )
1898 Lang::FontBinding::FontBinding( const char * id, const Ast::DynamicBindingExpression * bindingExpr, const RefCountPtr< const Lang::Font > & font )
1899 : bindingExpr_( bindingExpr ), font_( font ), id_( id )
1902 Lang::FontBinding::~FontBinding( )
1905 void
1906 Lang::FontBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
1908 if( *sysBindings == 0 )
1910 *sysBindings = new Kernel::SystemDynamicVariables( );
1911 Kernel::TextState * newState = new Kernel::TextState( );
1912 newState->font_ = font_;
1913 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1914 return;
1917 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
1919 Kernel::TextState * newState = new Kernel::TextState( );
1920 newState->font_ = font_;
1921 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1922 return;
1925 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
1927 if( newState->font_ != NullPtr< const Lang::Font >( ) )
1929 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
1932 newState->font_ = font_;
1933 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1936 void
1937 Lang::FontBinding::show( std::ostream & os ) const
1939 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":" ;
1940 font_->show( os );
1943 void
1944 Lang::FontBinding::gcMark( Kernel::GCMarkedSet & marked )
1946 const_cast< Lang::Font * >( font_.getPtr( ) )->gcMark( marked );
1950 Lang::TextSizeBinding::TextSizeBinding( const char * id, const Ast::DynamicBindingExpression * bindingExpr, const Concrete::Length size )
1951 : bindingExpr_( bindingExpr ), size_( size ), id_( id )
1954 Lang::TextSizeBinding::~TextSizeBinding( )
1957 void
1958 Lang::TextSizeBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
1960 if( *sysBindings == 0 )
1962 *sysBindings = new Kernel::SystemDynamicVariables( );
1963 Kernel::TextState * newState = new Kernel::TextState( );
1964 newState->size_ = size_;
1965 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1966 return;
1969 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
1971 Kernel::TextState * newState = new Kernel::TextState( );
1972 newState->size_ = size_;
1973 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1974 return;
1977 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
1979 if( ! IS_NAN( newState->size_ ) )
1981 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
1984 newState->size_ = size_;
1985 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1988 void
1989 Lang::TextSizeBinding::show( std::ostream & os ) const
1991 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":"
1992 << size_ / Interaction::displayUnit << Interaction::displayUnitName ;
1995 void
1996 Lang::TextSizeBinding::gcMark( Kernel::GCMarkedSet & marked )
2000 Lang::TextRenderingModeBinding::TextRenderingModeBinding( const char * id, const Ast::DynamicBindingExpression * bindingExpr, const Lang::TextRenderingMode::ValueType mode )
2001 : bindingExpr_( bindingExpr ), mode_( mode ), id_( id )
2004 Lang::TextRenderingModeBinding::~TextRenderingModeBinding( )
2007 void
2008 Lang::TextRenderingModeBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
2010 if( *sysBindings == 0 )
2012 *sysBindings = new Kernel::SystemDynamicVariables( );
2013 Kernel::TextState * newState = new Kernel::TextState( );
2014 newState->mode_ = mode_;
2015 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2016 return;
2019 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
2021 Kernel::TextState * newState = new Kernel::TextState( );
2022 newState->mode_ = mode_;
2023 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2024 return;
2027 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
2029 if( newState->mode_ != Lang::TextRenderingMode::UNDEFINED )
2031 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
2034 newState->mode_ = mode_;
2035 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2038 void
2039 Lang::TextRenderingModeBinding::show( std::ostream & os ) const
2041 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":"
2042 << "<internal-enum>" ;
2045 void
2046 Lang::TextRenderingModeBinding::gcMark( Kernel::GCMarkedSet & marked )
2050 Lang::TextRiseBinding::TextRiseBinding( const char * id, const Ast::DynamicBindingExpression * bindingExpr, const Concrete::Length ty )
2051 : bindingExpr_( bindingExpr ), ty_( ty ), isRelative_( false ), id_( id )
2054 Lang::TextRiseBinding::TextRiseBinding( const char * id, const Ast::DynamicBindingExpression * bindingExpr, const double r )
2055 : bindingExpr_( bindingExpr ), ty_( r ), isRelative_( true ), id_( id )
2058 Lang::TextRiseBinding::~TextRiseBinding( )
2061 void
2062 Lang::TextRiseBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
2064 if( *sysBindings == 0 )
2066 *sysBindings = new Kernel::SystemDynamicVariables( );
2067 Kernel::TextState * newState = new Kernel::TextState( );
2068 if( isRelative_ )
2070 newState->setRise( Concrete::Length::offtype( ty_ ) );
2072 else
2074 newState->setRise( ty_ );
2076 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2077 return;
2080 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
2082 Kernel::TextState * newState = new Kernel::TextState( );
2083 if( isRelative_ )
2085 newState->setRise( Concrete::Length::offtype( ty_ ) );
2087 else
2089 newState->setRise( ty_ );
2091 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2092 return;
2095 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
2097 if( newState->hasRise( ) )
2099 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
2102 if( isRelative_ )
2104 newState->setRise( Concrete::Length::offtype( ty_ ) );
2106 else
2108 newState->setRise( ty_ );
2110 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2113 void
2114 Lang::TextRiseBinding::show( std::ostream & os ) const
2116 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":" ;
2117 if( isRelative_ )
2119 os << Concrete::Length::offtype( ty_ ) ;
2121 else
2123 os << ty_ / Interaction::displayUnit << Interaction::displayUnitName ;
2127 void
2128 Lang::TextRiseBinding::gcMark( Kernel::GCMarkedSet & marked )
2132 Lang::TextKnockoutBinding::TextKnockoutBinding( const char * id, const Ast::DynamicBindingExpression * bindingExpr, bool knockout )
2133 : bindingExpr_( bindingExpr ), knockout_( knockout ), id_( id )
2136 Lang::TextKnockoutBinding::~TextKnockoutBinding( )
2139 void
2140 Lang::TextKnockoutBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
2142 if( *sysBindings == 0 )
2144 *sysBindings = new Kernel::SystemDynamicVariables( );
2145 Kernel::TextState * newState = new Kernel::TextState( );
2146 newState->knockout_ = knockout_;
2147 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2148 return;
2151 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
2153 Kernel::TextState * newState = new Kernel::TextState( );
2154 newState->knockout_ = knockout_;
2155 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2156 return;
2159 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
2161 if( ( newState->knockout_ & Kernel::TextState::KNOCKOUT_UNDEFINED_BIT ) != 0 )
2163 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
2166 newState->knockout_ = ( knockout_ ? Kernel::TextState::KNOCKOUT_FLAG_BIT : 0 );
2167 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2170 void
2171 Lang::TextKnockoutBinding::show( std::ostream & os ) const
2173 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":"
2174 << ( knockout_ ? "true" : "false" ) ;
2177 void
2178 Lang::TextKnockoutBinding::gcMark( Kernel::GCMarkedSet & marked )
2182 Kernel::CharacterSpacingDynamicVariableProperties::CharacterSpacingDynamicVariableProperties( const char * name )
2183 : Kernel::DynamicVariableProperties( name )
2186 Kernel::CharacterSpacingDynamicVariableProperties::~CharacterSpacingDynamicVariableProperties( )
2189 Kernel::VariableHandle
2190 Kernel::CharacterSpacingDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2192 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2193 return Kernel::VariableHandle( new Kernel::Variable( textState->characterSpacing( ) ) );
2196 void
2197 Kernel::CharacterSpacingDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2199 RefCountPtr< const Lang::Value > valUntyped = val->getUntyped( );
2201 Kernel::ContRef cont = evalState->cont_;
2205 typedef const Lang::Length ArgType;
2206 RefCountPtr< ArgType > spacing = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2207 cont->takeValue( Kernel::ValueRef( new Lang::CharacterSpacingBinding( name_, bindingExpr, spacing->get( ) ) ),
2208 evalState );
2209 return;
2211 catch( const NonLocalExit::NotThisType & ball )
2213 /* Wrong type; never mind!.. but see below!
2219 typedef const Lang::Float ArgType;
2220 RefCountPtr< ArgType > spacing = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2221 cont->takeValue( Kernel::ValueRef( new Lang::CharacterSpacingBinding( name_, bindingExpr, spacing->val_ ) ),
2222 evalState );
2223 return;
2225 catch( const NonLocalExit::NotThisType & ball )
2227 /* Wrong type; never mind!.. but see below!
2231 throw Exceptions::TypeMismatch( bindingExpr->exprLoc( ), valUntyped->getTypeName( ), Helpers::typeSetString( Lang::Length::staticTypeName( ), Lang::Float::staticTypeName( ) ) );
2235 Kernel::WordSpacingDynamicVariableProperties::WordSpacingDynamicVariableProperties( const char * name )
2236 : Kernel::DynamicVariableProperties( name )
2239 Kernel::WordSpacingDynamicVariableProperties::~WordSpacingDynamicVariableProperties( )
2242 Kernel::VariableHandle
2243 Kernel::WordSpacingDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2245 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2246 return Kernel::VariableHandle( new Kernel::Variable( textState->wordSpacing( ) ) );
2249 void
2250 Kernel::WordSpacingDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2252 RefCountPtr< const Lang::Value > valUntyped = val->getUntyped( );
2254 Kernel::ContRef cont = evalState->cont_;
2258 typedef const Lang::Length ArgType;
2259 RefCountPtr< ArgType > spacing = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2260 cont->takeValue( Kernel::ValueRef( new Lang::WordSpacingBinding( name_, bindingExpr, spacing->get( ) ) ),
2261 evalState );
2262 return;
2264 catch( const NonLocalExit::NotThisType & ball )
2266 /* Wrong type; never mind!.. but see below!
2272 typedef const Lang::Float ArgType;
2273 RefCountPtr< ArgType > spacing = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2274 cont->takeValue( Kernel::ValueRef( new Lang::WordSpacingBinding( name_, bindingExpr, spacing->val_ ) ),
2275 evalState );
2276 return;
2278 catch( const NonLocalExit::NotThisType & ball )
2280 /* Wrong type; never mind!.. but see below!
2284 throw Exceptions::TypeMismatch( bindingExpr->exprLoc( ), valUntyped->getTypeName( ), Helpers::typeSetString( Lang::Length::staticTypeName( ), Lang::Float::staticTypeName( ) ) );
2288 Kernel::HorizontalScalingDynamicVariableProperties::HorizontalScalingDynamicVariableProperties( const char * name )
2289 : Kernel::DynamicVariableProperties( name )
2292 Kernel::HorizontalScalingDynamicVariableProperties::~HorizontalScalingDynamicVariableProperties( )
2295 Kernel::VariableHandle
2296 Kernel::HorizontalScalingDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2298 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2299 return Kernel::VariableHandle( Shapes::Helpers::newValHandle( new Lang::Float( textState->horizontalScaling_ ) ) );
2302 void
2303 Kernel::HorizontalScalingDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2305 RefCountPtr< const Lang::Float > scaling = val->getVal< const Lang::Float >( bindingExpr->exprLoc( ) );
2306 Kernel::ContRef cont = evalState->cont_;
2307 cont->takeValue( Kernel::ValueRef( new Lang::HorizontalScalingBinding( name_, bindingExpr, scaling->val_ ) ),
2308 evalState );
2312 Kernel::LeadingDynamicVariableProperties::LeadingDynamicVariableProperties( const char * name )
2313 : Kernel::DynamicVariableProperties( name )
2316 Kernel::LeadingDynamicVariableProperties::~LeadingDynamicVariableProperties( )
2319 Kernel::VariableHandle
2320 Kernel::LeadingDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2322 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2323 return Kernel::VariableHandle( new Kernel::Variable( textState->leading( ) ) );
2326 void
2327 Kernel::LeadingDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2329 RefCountPtr< const Lang::Value > valUntyped = val->getUntyped( );
2331 Kernel::ContRef cont = evalState->cont_;
2335 typedef const Lang::Length ArgType;
2336 RefCountPtr< ArgType > ty = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2337 cont->takeValue( Kernel::ValueRef( new Lang::LeadingBinding( name_, bindingExpr, ty->get( ) ) ),
2338 evalState );
2339 return;
2341 catch( const NonLocalExit::NotThisType & ball )
2343 /* Wrong type; never mind!.. but see below!
2349 typedef const Lang::Float ArgType;
2350 RefCountPtr< ArgType > ty = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2351 cont->takeValue( Kernel::ValueRef( new Lang::LeadingBinding( name_, bindingExpr, ty->val_ ) ),
2352 evalState );
2353 return;
2355 catch( const NonLocalExit::NotThisType & ball )
2357 /* Wrong type; never mind!.. but see below!
2361 throw Exceptions::TypeMismatch( bindingExpr->exprLoc( ), valUntyped->getTypeName( ), Helpers::typeSetString( Lang::Length::staticTypeName( ), Lang::Float::staticTypeName( ) ) );
2365 Kernel::FontDynamicVariableProperties::FontDynamicVariableProperties( const char * name )
2366 : Kernel::DynamicVariableProperties( name )
2369 Kernel::FontDynamicVariableProperties::~FontDynamicVariableProperties( )
2372 Kernel::VariableHandle
2373 Kernel::FontDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2375 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2376 return Kernel::VariableHandle( new Kernel::Variable( textState->font_ ) );
2379 void
2380 Kernel::FontDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2382 Kernel::ContRef cont = evalState->cont_;
2383 cont->takeValue( Kernel::ValueRef( new Lang::FontBinding( name_, bindingExpr, val->getVal< const Lang::Font >( bindingExpr->exprLoc( ) ) ) ),
2384 evalState );
2388 Kernel::TextSizeDynamicVariableProperties::TextSizeDynamicVariableProperties( const char * name )
2389 : Kernel::DynamicVariableProperties( name )
2392 Kernel::TextSizeDynamicVariableProperties::~TextSizeDynamicVariableProperties( )
2395 Kernel::VariableHandle
2396 Kernel::TextSizeDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2398 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2399 return Kernel::VariableHandle( Shapes::Helpers::newValHandle( new Lang::Length( textState->size_ ) ) );
2402 void
2403 Kernel::TextSizeDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2405 RefCountPtr< const Lang::Length > size = val->getVal< const Lang::Length >( bindingExpr->exprLoc( ) );
2406 Kernel::ContRef cont = evalState->cont_;
2407 cont->takeValue( Kernel::ValueRef( new Lang::TextSizeBinding( name_, bindingExpr, size->get( ) ) ),
2408 evalState );
2412 Kernel::TextRenderingModeDynamicVariableProperties::TextRenderingModeDynamicVariableProperties( const char * name )
2413 : Kernel::DynamicVariableProperties( name )
2416 Kernel::TextRenderingModeDynamicVariableProperties::~TextRenderingModeDynamicVariableProperties( )
2419 Kernel::VariableHandle
2420 Kernel::TextRenderingModeDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2422 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2423 return Kernel::VariableHandle( Shapes::Helpers::newValHandle( new Lang::TextRenderingMode( textState->mode_ ) ) );
2426 void
2427 Kernel::TextRenderingModeDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2429 RefCountPtr< const Lang::TextRenderingMode > mode = val->getVal< const Lang::TextRenderingMode >( bindingExpr->exprLoc( ) );
2430 Kernel::ContRef cont = evalState->cont_;
2431 cont->takeValue( Kernel::ValueRef( new Lang::TextRenderingModeBinding( name_, bindingExpr, mode->mode_ ) ),
2432 evalState );
2436 Kernel::TextRiseDynamicVariableProperties::TextRiseDynamicVariableProperties( const char * name )
2437 : Kernel::DynamicVariableProperties( name )
2440 Kernel::TextRiseDynamicVariableProperties::~TextRiseDynamicVariableProperties( )
2443 Kernel::VariableHandle
2444 Kernel::TextRiseDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2446 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2447 return Kernel::VariableHandle( new Kernel::Variable( textState->rise( ) ) );
2450 void
2451 Kernel::TextRiseDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2453 RefCountPtr< const Lang::Value > valUntyped = val->getUntyped( );
2455 Kernel::ContRef cont = evalState->cont_;
2459 typedef const Lang::Length ArgType;
2460 RefCountPtr< ArgType > ty = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2461 cont->takeValue( Kernel::ValueRef( new Lang::TextRiseBinding( name_, bindingExpr, ty->get( ) ) ),
2462 evalState );
2463 return;
2465 catch( const NonLocalExit::NotThisType & ball )
2467 /* Wrong type; never mind!.. but see below!
2473 typedef const Lang::Float ArgType;
2474 RefCountPtr< ArgType > ty = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2475 cont->takeValue( Kernel::ValueRef( new Lang::TextRiseBinding( name_, bindingExpr, ty->val_ ) ),
2476 evalState );
2477 return;
2479 catch( const NonLocalExit::NotThisType & ball )
2481 /* Wrong type; never mind!.. but see below!
2485 throw Exceptions::TypeMismatch( bindingExpr->exprLoc( ), valUntyped->getTypeName( ), Helpers::typeSetString( Lang::Length::staticTypeName( ), Lang::Float::staticTypeName( ) ) );
2489 Kernel::TextKnockoutDynamicVariableProperties::TextKnockoutDynamicVariableProperties( const char * name )
2490 : Kernel::DynamicVariableProperties( name )
2493 Kernel::TextKnockoutDynamicVariableProperties::~TextKnockoutDynamicVariableProperties( )
2496 Kernel::VariableHandle
2497 Kernel::TextKnockoutDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2499 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2500 return Kernel::VariableHandle( Shapes::Helpers::newValHandle( new Lang::Boolean( ( textState->knockout_ & Kernel::TextState::KNOCKOUT_FLAG_BIT ) != 0 ) ) );
2503 void
2504 Kernel::TextKnockoutDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2506 RefCountPtr< const Lang::Boolean > mode = val->getVal< const Lang::Boolean >( bindingExpr->exprLoc( ) );
2507 Kernel::ContRef cont = evalState->cont_;
2508 cont->takeValue( Kernel::ValueRef( new Lang::TextKnockoutBinding( name_, bindingExpr, mode->val_ ) ),
2509 evalState );
2514 Kernel::TextState::TextState( )
2515 : relativeFlags_( 0 ),
2516 characterSpacing_( Concrete::Length( std::numeric_limits< double >::signaling_NaN( ) ) ),
2517 wordSpacing_( Concrete::Length( std::numeric_limits< double >::signaling_NaN( ) ) ),
2518 horizontalScaling_( std::numeric_limits< double >::signaling_NaN( ) ),
2519 leading_( Concrete::Length( std::numeric_limits< double >::signaling_NaN( ) ) ),
2520 rise_( Concrete::Length( std::numeric_limits< double >::signaling_NaN( ) ) ),
2521 font_( NullPtr< const Lang::Font >( ) ),
2522 size_( Concrete::Length( std::numeric_limits< double >::signaling_NaN( ) ) ),
2523 mode_( Lang::TextRenderingMode::UNDEFINED ),
2524 knockout_( KNOCKOUT_UNDEFINED_BIT )
2527 Kernel::TextState::TextState( const Kernel::TextState & orig )
2528 : relativeFlags_( orig.relativeFlags_ ),
2529 characterSpacing_( orig.characterSpacing_ ),
2530 wordSpacing_( orig.wordSpacing_ ),
2531 horizontalScaling_( orig.horizontalScaling_ ),
2532 leading_( orig.leading_ ),
2533 rise_( orig.rise_ ),
2534 font_( orig.font_ ),
2535 size_( orig.size_ ),
2536 mode_( orig.mode_ ),
2537 knockout_( orig.knockout_ )
2540 Kernel::TextState::TextState( const Kernel::TextState & newValues, const Kernel::TextState & oldValues )
2541 : relativeFlags_( oldValues.relativeFlags_ ),
2542 characterSpacing_( oldValues.characterSpacing_ ),
2543 wordSpacing_( oldValues.wordSpacing_ ),
2544 horizontalScaling_( oldValues.horizontalScaling_ ),
2545 leading_( oldValues.leading_ ),
2546 rise_( oldValues.rise_ ),
2547 font_( oldValues.font_ ),
2548 size_( oldValues.size_ ),
2549 mode_( oldValues.mode_ ),
2550 knockout_( oldValues.knockout_ )
2552 if( newValues.hasCharacterSpacing( ) )
2554 characterSpacing_ = newValues.characterSpacing_;
2555 relativeFlags_ = ( newValues.characterSpacingIsRelative( ) ? ( relativeFlags_ | RELATIVE_CHARACTER_SPACING ) : ( relativeFlags_ & ~RELATIVE_CHARACTER_SPACING ) );
2557 if( newValues.hasWordSpacing( ) )
2559 wordSpacing_ = newValues.wordSpacing_;
2560 relativeFlags_ = ( newValues.wordSpacingIsRelative( ) ? ( relativeFlags_ | RELATIVE_WORD_SPACING ) : ( relativeFlags_ & ~RELATIVE_WORD_SPACING ) );
2562 if( ! IS_NAN( newValues.horizontalScaling_ ) )
2564 horizontalScaling_ = newValues.horizontalScaling_;
2566 if( newValues.hasLeading( ) )
2568 leading_ = newValues.leading_;
2569 relativeFlags_ = ( newValues.leadingIsRelative( ) ? ( relativeFlags_ | RELATIVE_LEADING ) : ( relativeFlags_ & ~RELATIVE_LEADING ) );
2571 if( newValues.hasRise( ) )
2573 rise_ = newValues.rise_;
2574 relativeFlags_ = ( newValues.riseIsRelative( ) ? ( relativeFlags_ | RELATIVE_RISE ) : ( relativeFlags_ & ~RELATIVE_RISE ) );
2576 if( newValues.font_ != NullPtr< const Lang::Font >( ) )
2578 font_ = newValues.font_;
2580 if( ! IS_NAN( newValues.size_ ) )
2582 size_ = newValues.size_;
2584 if( newValues.mode_ != Lang::TextRenderingMode::UNDEFINED )
2586 mode_ = newValues.mode_;
2588 if( ! ( newValues.knockout_ & KNOCKOUT_UNDEFINED_BIT ) )
2590 knockout_ = newValues.knockout_;
2594 std::map< bool, RefCountPtr< SimplePDF::PDF_Object > > Kernel::TextState::knockoutNameMap_;
2596 Kernel::TextState::TextState( bool setDefaults )
2597 : relativeFlags_( RELATIVE_LEADING ),
2598 characterSpacing_( 0 ),
2599 wordSpacing_( 0 ),
2600 horizontalScaling_( 1 ),
2601 leading_( 1 ),
2602 rise_( 0 ),
2603 font_( Lang::THE_FONT_HELVETICA ),
2604 size_( Concrete::Length( 10 ) ),
2605 mode_( Lang::TextRenderingMode::FILL ),
2606 knockout_( 0 ) // this means false
2608 if( ! setDefaults )
2610 throw Exceptions::InternalError( strrefdup( "setDefaults must be true in TextState::TextState." ) );
2614 Kernel::TextState::~TextState( )
2617 void
2618 Kernel::TextState::print( std::ostream & os, const std::string & indentation ) const
2620 if( font_ != NullPtr< const Lang::Font >( ) )
2622 os << indentation << Interaction::DYNAMIC_VARIABLE_PREFIX << Lang::DYNAMIC_VARIABLE_ID_TEXT_FONT << ": " << font_->fontName( ) << std::endl ;
2624 if( ! IS_NAN( size_ ) )
2626 os << indentation << Interaction::DYNAMIC_VARIABLE_PREFIX << Lang::DYNAMIC_VARIABLE_ID_TEXT_SIZE << ": " << size_ / Interaction::displayUnit << Interaction::displayUnitName << std::endl ;
2628 os << "(and a bunch of other variables...)" << std::endl ;
2632 void
2633 Kernel::TextState::setLeading( const Concrete::Length leading )
2635 relativeFlags_ &= ~ RELATIVE_LEADING;
2636 leading_ = leading;
2639 void
2640 Kernel::TextState::setLeading( const double relativeLeading )
2642 relativeFlags_ |= RELATIVE_LEADING;
2643 leading_ = Concrete::Length( relativeLeading ); // We must keep track of this type trick by always looking at relativeFlags_ & RELATIVE_LEADING.
2646 RefCountPtr< const Lang::Value >
2647 Kernel::TextState::leading( ) const
2649 if( leadingIsRelative( ) )
2651 return RefCountPtr< const Lang::Value >( new Lang::Float( Concrete::Length::offtype( leading_ ) ) );
2653 return RefCountPtr< const Lang::Value >( new Lang::Length( leading_ ) );
2656 Concrete::Length
2657 Kernel::TextState::leadingConcrete( ) const
2659 if( leadingIsRelative( ) )
2661 return Concrete::Length::offtype( leading_ ) * size_;
2663 return leading_;
2666 bool
2667 Kernel::TextState::hasLeading( ) const
2669 return ! IS_NAN( leading_ );
2672 bool
2673 Kernel::TextState::leadingIsRelative( ) const
2675 return ( relativeFlags_ & RELATIVE_LEADING ) != 0;
2679 void
2680 Kernel::TextState::setRise( const Concrete::Length rise )
2682 relativeFlags_ &= ~ RELATIVE_RISE;
2683 rise_ = rise;
2686 void
2687 Kernel::TextState::setRise( const double relativeRise )
2689 relativeFlags_ |= RELATIVE_RISE;
2690 rise_ = Concrete::Length( relativeRise ); // We must keep track of this type trick by always looking at relativeFlags_ & RELATIVE_RISE.
2693 RefCountPtr< const Lang::Value >
2694 Kernel::TextState::rise( ) const
2696 if( riseIsRelative( ) )
2698 return RefCountPtr< const Lang::Value >( new Lang::Float( Concrete::Length::offtype( rise_ ) ) );
2700 return RefCountPtr< const Lang::Value >( new Lang::Length( rise_ ) );
2703 Concrete::Length
2704 Kernel::TextState::riseConcrete( ) const
2706 if( riseIsRelative( ) )
2708 return Concrete::Length::offtype( rise_ ) * size_;
2710 return rise_;
2713 bool
2714 Kernel::TextState::hasRise( ) const
2716 return ! IS_NAN( rise_ );
2719 bool
2720 Kernel::TextState::riseIsRelative( ) const
2722 return ( relativeFlags_ & RELATIVE_RISE ) != 0;
2726 void
2727 Kernel::TextState::setCharacterSpacing( const Concrete::Length spacing )
2729 relativeFlags_ &= ~ RELATIVE_CHARACTER_SPACING;
2730 characterSpacing_ = spacing;
2733 void
2734 Kernel::TextState::setCharacterSpacing( const double relativeSpacing )
2736 relativeFlags_ |= RELATIVE_CHARACTER_SPACING;
2737 characterSpacing_ = Concrete::Length( relativeSpacing ); // We must keep track of this type trick by always looking at the relevant bit in relativeFlags_.
2740 RefCountPtr< const Lang::Value >
2741 Kernel::TextState::characterSpacing( ) const
2743 if( characterSpacingIsRelative( ) )
2745 return RefCountPtr< const Lang::Value >( new Lang::Float( Concrete::Length::offtype( characterSpacing_ ) ) );
2747 return RefCountPtr< const Lang::Value >( new Lang::Length( characterSpacing_ ) );
2750 Concrete::Length
2751 Kernel::TextState::characterSpacingConcrete( ) const
2753 if( characterSpacingIsRelative( ) )
2755 return Concrete::Length::offtype( characterSpacing_ ) * size_;
2757 return characterSpacing_;
2760 bool
2761 Kernel::TextState::hasCharacterSpacing( ) const
2763 return ! IS_NAN( characterSpacing_ );
2766 bool
2767 Kernel::TextState::characterSpacingIsRelative( ) const
2769 return ( relativeFlags_ & RELATIVE_CHARACTER_SPACING ) != 0;
2773 void
2774 Kernel::TextState::setWordSpacing( const Concrete::Length spacing )
2776 relativeFlags_ &= ~ RELATIVE_WORD_SPACING;
2777 wordSpacing_ = spacing;
2780 void
2781 Kernel::TextState::setWordSpacing( const double relativeSpacing )
2783 relativeFlags_ |= RELATIVE_WORD_SPACING;
2784 wordSpacing_ = Concrete::Length( relativeSpacing ); // We must keep track of this type trick by always looking at the relevant bit in relativeFlags_.
2787 RefCountPtr< const Lang::Value >
2788 Kernel::TextState::wordSpacing( ) const
2790 if( wordSpacingIsRelative( ) )
2792 return RefCountPtr< const Lang::Value >( new Lang::Float( Concrete::Length::offtype( wordSpacing_ ) ) );
2794 return RefCountPtr< const Lang::Value >( new Lang::Length( wordSpacing_ ) );
2797 Concrete::Length
2798 Kernel::TextState::wordSpacingConcrete( ) const
2800 if( wordSpacingIsRelative( ) )
2802 return Concrete::Length::offtype( wordSpacing_ ) * size_;
2804 return wordSpacing_;
2807 bool
2808 Kernel::TextState::hasWordSpacing( ) const
2810 return ! IS_NAN( wordSpacing_ );
2813 bool
2814 Kernel::TextState::wordSpacingIsRelative( ) const
2816 return ( relativeFlags_ & RELATIVE_WORD_SPACING ) != 0;
2820 bool
2821 Kernel::TextState::synchAssertKnockout( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool clip, bool force )
2823 assertKnockout( ref );
2824 return synchButKnockout( os, ref, resources, clip, force );
2827 bool
2828 Kernel::TextState::synchCharacterSpacing( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool force )
2830 if( force ||
2831 characterSpacing_ != ref->characterSpacing_ ||
2832 characterSpacingIsRelative( ) != ref->characterSpacingIsRelative( ) )
2834 if( ! ref->hasCharacterSpacing( ) )
2836 return false;
2838 relativeFlags_ = ( ref->characterSpacingIsRelative( ) ? ( relativeFlags_ | RELATIVE_CHARACTER_SPACING ) : ( relativeFlags_ & ~RELATIVE_CHARACTER_SPACING ) );
2839 characterSpacing_ = ref->characterSpacing_;
2840 if( characterSpacingIsRelative( ) )
2842 os << Concrete::Length::offtype( characterSpacing_ ) * Concrete::Length::offtype( ref->size_ ) << " Tc " ;
2844 else
2846 os << Concrete::Length::offtype( characterSpacing_ ) << " Tc " ;
2848 return true;
2850 return false;
2853 bool
2854 Kernel::TextState::synchWordSpacing( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool force )
2856 if( force ||
2857 wordSpacing_ != ref->wordSpacing_ ||
2858 wordSpacingIsRelative( ) != ref->wordSpacingIsRelative( ) )
2860 if( ! ref->hasWordSpacing( ) )
2862 return false;
2864 relativeFlags_ = ( ref->wordSpacingIsRelative( ) ? ( relativeFlags_ | RELATIVE_WORD_SPACING ) : ( relativeFlags_ & ~RELATIVE_WORD_SPACING ) );
2865 wordSpacing_ = ref->wordSpacing_;
2866 if( wordSpacingIsRelative( ) )
2868 os << Concrete::Length::offtype( wordSpacing_ ) * Concrete::Length::offtype( ref->size_ ) << " Tw " ;
2870 else
2872 os << Concrete::Length::offtype( wordSpacing_ ) << " Tw " ;
2874 return true;
2876 return false;
2879 bool
2880 Kernel::TextState::synchHorizontalScaling( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool force )
2882 if( force || horizontalScaling_ != ref->horizontalScaling_ )
2884 if( IS_NAN( ref->horizontalScaling_ ) )
2886 return false;
2888 horizontalScaling_ = ref->horizontalScaling_;
2889 os << 100 * horizontalScaling_ << " Tz " ;
2890 return true;
2892 return false;
2895 bool
2896 Kernel::TextState::synchLeading( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool force )
2898 if( force ||
2899 leading_ != ref->leading_ ||
2900 leadingIsRelative( ) != ref->leadingIsRelative( ) )
2902 if( ! ref->hasLeading( ) )
2904 return false;
2906 relativeFlags_ = ( ref->leadingIsRelative( ) ? ( relativeFlags_ | RELATIVE_LEADING ) : ( relativeFlags_ & ~RELATIVE_LEADING ) );
2907 leading_ = ref->leading_;
2908 if( leadingIsRelative( ) )
2910 os << Concrete::Length::offtype( leading_ ) * Concrete::Length::offtype( ref->size_ ) << " TL " ;
2912 else
2914 os << Concrete::Length::offtype( leading_ ) << " TL " ;
2916 return true;
2918 return false;
2921 bool
2922 Kernel::TextState::synchFontAndSize( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool force )
2924 if( force ||
2925 font_ != ref->font_ ||
2926 size_ != ref->size_ )
2928 if( ref->font_ == NullPtr< const Lang::Font >( ) &&
2929 IS_NAN( ref->size_ ) )
2931 return false;
2933 if( ref->font_ == NullPtr< const Lang::Font >( ) ||
2934 IS_NAN( ref->size_ ) )
2936 throw Exceptions::MiscellaneousRequirement( "It is impossible to leave unspecified only one of font and size." );
2938 bool sizeChanged = ( size_ != ref->size_ );
2939 font_ = ref->font_;
2940 size_ = ref->size_;
2941 os << resources->nameofFont( font_->resource( ) ) << " " << Concrete::Length::offtype( size_ ) << " Tf " ;
2942 if( sizeChanged )
2944 if( ref->hasLeading( ) && ref->leadingIsRelative( ) )
2946 synchLeading( os, ref, resources, true );
2948 if( ref->hasRise( ) && ref->riseIsRelative( ) )
2950 synchRise( os, ref, resources, true );
2952 if( ref->hasCharacterSpacing( ) && ref->characterSpacingIsRelative( ) )
2954 synchCharacterSpacing( os, ref, resources, true );
2956 if( ref->hasWordSpacing( ) && ref->wordSpacingIsRelative( ) )
2958 synchWordSpacing( os, ref, resources, true );
2961 return true;
2963 return false;
2966 bool
2967 Kernel::TextState::synchMode( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool clip, bool force )
2969 Lang::TextRenderingMode::ValueType refMode = clip ? Lang::TextRenderingMode::clipMode( ref->mode_ ) : ref->mode_ ;
2970 if( force || mode_ != refMode )
2972 if( ref->mode_ == Lang::TextRenderingMode::UNDEFINED )
2974 return false;
2976 mode_ = refMode;
2977 os << mode_ << " Tr " ;
2978 return true;
2980 return false;
2983 bool
2984 Kernel::TextState::synchRise( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool force )
2986 if( force ||
2987 rise_ != ref->rise_ ||
2988 riseIsRelative( ) != ref->riseIsRelative( ) )
2990 if( ! ref->hasRise( ) )
2992 return false;
2994 relativeFlags_ = ( ref->riseIsRelative( ) ? ( relativeFlags_ | RELATIVE_RISE ) : ( relativeFlags_ & ~RELATIVE_RISE ) );
2995 rise_ = ref->rise_;
2996 if( riseIsRelative( ) )
2998 os << Concrete::Length::offtype( rise_ ) * Concrete::Length::offtype( ref->size_ ) << " Ts " ;
3000 else
3002 os << Concrete::Length::offtype( rise_ ) << " Ts " ;
3004 return true;
3006 return false;
3009 bool
3010 Kernel::TextState::synchKnockout( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool force )
3012 if( force || knockout_ != ref->knockout_ )
3014 if( ( ref->knockout_ & Kernel::TextState::KNOCKOUT_UNDEFINED_BIT ) != 0 )
3016 return false;
3018 const SimplePDF::PDF_Version::Version KNOCKOUT_VERSION = SimplePDF::PDF_Version::PDF_1_4;
3019 if( ! Kernel::the_PDF_version.greaterOrEqual( KNOCKOUT_VERSION ) )
3021 Kernel::the_PDF_version.message( KNOCKOUT_VERSION, "The text state knockout mode setting was ignored." );
3023 knockout_ = ref->knockout_;
3024 typedef typeof knockoutNameMap_ MapType;
3025 MapType::const_iterator i = knockoutNameMap_.find( knockout_ );
3026 if( i != knockoutNameMap_.end( ) )
3028 os << resources->nameofGraphicsState( i->second ) << " gs " ;
3030 else
3032 RefCountPtr< SimplePDF::PDF_Dictionary > dic;
3033 (*dic)[ "Type" ] = SimplePDF::newName( "ExtGState" );
3034 (*dic)[ "TK" ] = SimplePDF::newBoolean( knockout_ );
3036 RefCountPtr< SimplePDF::PDF_Object > indirection = SimplePDF::indirect( dic, & Kernel::theIndirectObjectCount );
3037 knockoutNameMap_.insert( MapType::value_type( knockout_, indirection ) );
3039 os << resources->nameofGraphicsState( indirection ) << " gs " ;
3041 return true;
3043 return false;
3046 void
3047 Kernel::TextState::assertKnockout( const Kernel::TextState * ref )
3049 if( knockout_ != ref->knockout_ )
3051 throw Exceptions::MiscellaneousRequirement( "PDF does not allow the text knockout mode to change within a text object." );
3055 bool
3056 Kernel::TextState::synchButKnockout( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool clip, bool force )
3058 bool anyChange = false;
3059 anyChange = synchCharacterSpacing( os, ref, resources, force ) || anyChange;
3060 anyChange = synchWordSpacing( os, ref, resources, force ) || anyChange;
3061 anyChange = synchHorizontalScaling( os, ref, resources, force ) || anyChange;
3062 anyChange = synchFontAndSize( os, ref, resources, force ) || anyChange;
3063 anyChange = synchLeading( os, ref, resources, force ) || anyChange; // It is important that this is done after synching the size!
3064 anyChange = synchMode( os, ref, resources, clip, force ) || anyChange;
3065 anyChange = synchRise( os, ref, resources, force ) || anyChange;
3066 if( anyChange )
3068 os << std::endl ;
3070 return anyChange;
3074 Lang::FontMethod_glyph::FontMethod_glyph( RefCountPtr< const Lang::Font > self, const Ast::FileID * fullMethodID )
3075 : Lang::MethodBase< class_type >( self, fullMethodID, false, true )
3077 formals_->appendEvaluatedCoreFormal( "char", Kernel::THE_SLOT_VARIABLE );
3080 Lang::FontMethod_glyph::~FontMethod_glyph( )
3083 void
3084 Lang::FontMethod_glyph::call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const
3086 args.applyDefaults( );
3088 size_t argsi = 0;
3089 typedef const Lang::Character ArgType;
3091 Kernel::ContRef cont = evalState->cont_;
3092 cont->takeValue( self_->getGlyph( Helpers::down_cast_CoreArgument< ArgType >( title_, args, argsi, callLoc )->val_,
3093 evalState->dyn_->getTextState( )->size_ ),
3094 evalState );
3095 return;