Update procedures
[shapes.git] / source / texttypes.cc
blobca94f5d85bd6844112105f23aa5a6cae8ed87275
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 "warn.h"
36 #include "config.h"
38 #include <fstream>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <stdint.h>
42 #include <cstddef>
43 #include <iomanip>
45 #ifdef HAVE_FT2
46 #include FT_OUTLINE_H
47 #endif
49 using namespace Shapes;
51 std::map< RefCountPtr< const char >, RefCountPtr< SimplePDF::PDF_Object >, charRefPtrLess > Lang::Font::theFontResourceMap_;
52 std::map< RefCountPtr< const char >, RefCountPtr< const FontMetrics::FontMetric >, charRefPtrLess > Lang::Font::theFontMetricsMap_;
53 std::list< std::string > Lang::Font::theFontMetricsSearchPath_;
55 namespace Shapes
57 namespace Helpers
59 Kernel::StructureFactory FontMethod_glyph_resultFactory( "odd?", "paths", "advance" );
63 SimplePDF::PDF_ToUnicode::PDF_ToUnicode( RefCountPtr< CharSet > charSet, const RefCountPtr< const Shapes::Lang::Font > & font )
64 : charSet_( charSet ),
65 font_( font )
67 dic[ "Filter" ] = SimplePDF::newName( "FlateDecode" );
70 SimplePDF::PDF_ToUnicode::~PDF_ToUnicode( )
71 { }
73 void
74 SimplePDF::PDF_ToUnicode::writeTo( std::ostream & os, SimplePDF::PDF_xref * xref, const RefCountPtr< const PDF_Object > & self ) const
76 std::ostringstream & wdata = const_cast< std::ostringstream & >( data );
77 wdata << std::setfill( '0' );
78 wdata << "/CIDInit /ProcSet findresource begin" << std::endl
79 << "12 dict begin" << std::endl
80 << "begincmap" << std::endl
81 << "/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def" << std::endl
82 << "/CMapName /Adobe-Identity-UCS def" << std::endl
83 << "/CMapType 2 def" << std::endl
84 << "1 begincodespacerange" << std::endl
85 << "<0000> <FFFF>" << std::endl
86 << "endcodespacerange" << std::endl ;
87 const size_t bufSize = 10;
88 unsigned char buf[ bufSize ];
89 size_t remaining = charSet_->size( );
90 size_t room = std::min( static_cast< size_t >( 100 ), remaining );
91 remaining -= room;
92 wdata << room << " beginbfchar" << std::endl ;
93 for( CharSet::const_iterator i = charSet_->begin( ); i != charSet_->end( ); ++i, --room )
95 if( room == 0 )
97 wdata << "endbfchar" << std::endl ;
98 room = std::min( static_cast< size_t >( 100 ), remaining );
99 remaining -= room;
100 wdata << room << " beginbfchar" << std::endl ;
102 size_t out_avail = 2;
103 unsigned char * out_buf = buf;
104 font_->encode( *i, reinterpret_cast< char ** >( & out_buf ), & out_avail );
105 if( out_avail != 0 )
107 throw Exceptions::InternalError( "Expected the encoded character to occupy exactly two bytes." );
109 wdata << "<" << std::hex << std::setw( 2 ) << static_cast< int >( buf[0] ) << std::hex << std::setw( 2 ) << static_cast< int >( buf[1] ) << "> " ;
110 out_avail = bufSize;
111 out_buf = buf;
112 i->encode_UTF16BE( reinterpret_cast< char ** >( & out_buf ), & out_avail );
113 wdata << "<" ;
114 for( const unsigned char * c = buf; c != out_buf; ++c )
116 wdata << std::hex << std::setw( 2 ) << static_cast< int >( *c ) ;
118 wdata << ">" << std::endl ;
120 wdata << "endbfchar" << std::endl
121 << "endcmap" << std::endl
122 << "CMapName currentdict /CMap defineresource pop" << std::endl
123 << "end" << std::endl
124 << "end" ; /* No need to put a newline at the end. */
125 PDF_Stream_out::writeTo( os, xref, self );
128 SimplePDF::PDF_Widths::PDF_Widths( RefCountPtr< CharSet > charSet, const RefCountPtr< const Shapes::Lang::Font > & font )
129 : charSet_( charSet ),
130 font_( font )
133 SimplePDF::PDF_Widths::~PDF_Widths( )
136 void
137 SimplePDF::PDF_Widths::writeTo( std::ostream & os, SimplePDF::PDF_xref * xref, const RefCountPtr< const PDF_Object > & self ) const
139 const FontMetrics::WritingDirectionMetrics * metrics = font_->metrics( )->horizontalMetrics_.getPtr( );
140 const size_t bufSize = 2;
141 unsigned char buf[ bufSize ];
142 VecType & wvec = const_cast< VecType & >( vec );
143 FontMetrics::CharacterMetrics m( 0 );
144 for( CharSet::const_iterator i = charSet_->begin( ); i != charSet_->end( ); )
146 CharSet::const_iterator j = i;
147 CharSet::const_iterator last = i;
148 ++i;
149 for( ; i != charSet_->end( ) && i->get_UCS4( ) == last->get_UCS4( ) + 1; last = i, ++i )
151 size_t out_avail = bufSize;
152 unsigned char * out_buf = buf;
153 font_->encode( j->get_UCS4( ), reinterpret_cast< char ** >( & out_buf ), & out_avail );
154 if( out_avail != 0 )
156 throw Exceptions::InternalError( "Expected the encoded character to occupy exactly two bytes." );
158 wvec.push_back( SimplePDF::newInt( static_cast< SimplePDF::PDF_Int::ValueType >( buf[0] ) * 0x100 +
159 static_cast< SimplePDF::PDF_Int::ValueType >( buf[1] ) ) );
160 SimplePDF::PDF_Vector * sub_vec = new SimplePDF::PDF_Vector;
161 wvec.push_back( RefCountPtr< SimplePDF::PDF_Object >( sub_vec ) );
162 for( ; j != i; ++j )
164 sub_vec->vec.push_back( SimplePDF::newFloat( metrics->charByCode( j->get_UCS4( ), & m )->horizontalCharWidthX_* 1000 ) );
167 PDF_Vector::writeTo( os, xref, self );
170 namespace Shapes
173 namespace Lang
176 class FontMethod_glyph : public Lang::MethodBase< Lang::Font >
178 public:
179 FontMethod_glyph( RefCountPtr< const Lang::Font > _self, const Ast::FileID * fullMethodID );
180 virtual ~FontMethod_glyph( );
181 virtual void call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const;
182 static const char * staticFieldID( ) { return "glyph"; }
188 void
189 Font_register_methods( Lang::SystemFinalClass * dstClass )
191 dstClass->registerMethod( new Kernel::MethodFactory< Lang::Font, Lang::FontMethod_glyph >( ) );
194 void
195 Lang::Font::push_backFontMetricsPath( const std::string & path )
197 if( path.empty( ) ||
198 path[ path.size( ) - 1 ] == '/' )
200 theFontMetricsSearchPath_.push_back( path );
202 else
204 theFontMetricsSearchPath_.push_back( path + "/" );
208 std::string
209 Lang::Font::searchGlyphList( )
211 std::string res;
213 if( theFontMetricsSearchPath_.empty( ) )
215 throw Exceptions::ExternalError( strrefdup( "The font metrics path was not set up (needed for the glyph list). Consider defining the environment variable SHAPESFONTMETRICS." ) );
218 typedef typeof theFontMetricsSearchPath_ ListType;
219 for( ListType::const_iterator i = theFontMetricsSearchPath_.begin( ); i != theFontMetricsSearchPath_.end( ); ++i )
221 res = *i + "glyphlist.txt";
222 struct stat theStatDummy;
223 if( stat( res.c_str( ), & theStatDummy ) == 0 )
225 return res;
228 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." );
231 std::string
232 Lang::Font::searchFontMetrics( RefCountPtr< const char > fontName )
234 std::string res;
236 if( strlen( fontName.getPtr( ) ) == 0 )
238 throw Exceptions::InternalError( strrefdup( "Lang::Font::searchFontMetrics called with empty argument." ) );
241 if( *fontName.getPtr( ) == '/' )
243 throw Exceptions::InternalError( "The font name cannot begin with \"/\", as if is was an absolute path to something." );
246 if( theFontMetricsSearchPath_.empty( ) )
248 throw Exceptions::ExternalError( strrefdup( "The font metrics path was not set up. Consider defining the environment variable SHAPESFONTMETRICS." ) );
251 typedef typeof theFontMetricsSearchPath_ ListType;
252 for( ListType::const_iterator i = theFontMetricsSearchPath_.begin( ); i != theFontMetricsSearchPath_.end( ); ++i )
254 res = *i + fontName.getPtr( ) + ".afm";
255 struct stat theStatDummy;
256 if( stat( res.c_str( ), & theStatDummy ) == 0 )
258 return res;
261 throw Exceptions::MissingFontMetrics( fontName, & theFontMetricsSearchPath_ );
264 std::string
265 Lang::Font::searchCharacterEncoding( const char * encodingName )
267 std::string res;
269 if( theFontMetricsSearchPath_.empty( ) )
271 throw Exceptions::ExternalError( strrefdup( "The font metrics path was not set up. Consider defining the environment variable SHAPESFONTMETRICS." ) );
274 typedef typeof theFontMetricsSearchPath_ ListType;
275 for( ListType::const_iterator i = theFontMetricsSearchPath_.begin( ); i != theFontMetricsSearchPath_.end( ); ++i )
277 res = *i + encodingName + ".enc";
278 struct stat theStatDummy;
279 if( stat( res.c_str( ), & theStatDummy ) == 0 )
281 return res;
284 std::ostringstream msg;
285 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." ;
286 throw Exceptions::MiscellaneousRequirement( strrefdup( msg ) );
290 Lang::Font::Font( const RefCountPtr< const char > fontName, bool outline, RefCountPtr< SimplePDF::PDF_Object > & resource, RefCountPtr< const FontMetrics::FontMetric > metrics )
291 : fontName_( fontName ), outline_( outline ), resource_( resource ), metrics_( metrics )
293 // If this font has been instantiated before, it is reused.
295 typedef typeof theFontResourceMap_ MapType;
296 MapType::const_iterator i = theFontResourceMap_.find( fontName_ );
297 if( i != theFontResourceMap_.end( ) )
299 resource_ = i->second;
301 else
303 theFontResourceMap_.insert( MapType::value_type( fontName_, resource_ ) );
307 typedef typeof theFontMetricsMap_ MapType;
308 MapType::const_iterator i = theFontMetricsMap_.find( fontName_ );
309 if( i != theFontMetricsMap_.end( ) )
311 metrics_ = i->second;
313 else if( metrics_ != NullPtr< const FontMetrics::FontMetric >( ) )
315 theFontMetricsMap_.insert( MapType::value_type( fontName_, metrics_ ) );
320 // Note that we must not initialize name_ with builtinFontMap_[ fontConst ] here, since builtinFontMap_ may not be
321 // initialized when this constructor is invoked from globals.cc.
322 Lang::Font::Font( const RefCountPtr< const char > fontName )
323 : fontName_( fontName ), outline_( false ), resource_( NullPtr< SimplePDF::PDF_Object >( ) ), metrics_( NullPtr< const FontMetrics::FontMetric >( ) )
326 Lang::Font::~Font( )
329 Kernel::VariableHandle
330 Lang::Font::getField( const char * fieldID, const RefCountPtr< const Lang::Value > & selfRef ) const
332 // if( strcmp( fieldID, "CIDFont?" ) == 0 )
333 // {
334 // return Helpers::newValHandle( new Lang::Boolean( metrics_->isCIDFont_ ) );
335 // }
336 if( strcmp( fieldID, "kerning?" ) == 0 )
338 return Helpers::newValHandle( new Lang::Boolean( metrics_->hasKerning_ ) );
340 if( strcmp( fieldID, "family" ) == 0 )
342 return Kernel::VariableHandle( new Kernel::Variable( this->family_name( ) ) );
344 if( strcmp( fieldID, "style" ) == 0 )
346 return Kernel::VariableHandle( new Kernel::Variable( this->style_name( ) ) );
348 if( strcmp( fieldID, "PostScript_name" ) == 0 )
350 return Kernel::VariableHandle( new Kernel::Variable( this->PostScript_name( ) ) );
353 return TypeID->getMethod( selfRef, fieldID ); /* This will throw if there is no such method. */
356 RefCountPtr< const Lang::String >
357 Lang::Font::family_name( ) const
359 return RefCountPtr< const Lang::String >( new Lang::String( fontName_ ) );
362 RefCountPtr< const Lang::String >
363 Lang::Font::style_name( ) const
365 return RefCountPtr< const Lang::String >( new Lang::String( "", true ) );
368 RefCountPtr< const Lang::Symbol >
369 Lang::Font::PostScript_name( ) const
371 return RefCountPtr< const Lang::Symbol >( new Lang::Symbol( fontName_.getPtr( ) ) );
374 RefCountPtr< const Lang::Value >
375 Lang::Font::getGlyph( Kernel::UnicodeCodePoint c, Concrete::Length size ) const
377 throw Exceptions::MiscellaneousRequirement( "It is not possible to extract individual glyphs from this font." );
381 const RefCountPtr< SimplePDF::PDF_Object > &
382 Lang::Font::resource( ) const
384 if( resource_ != NullPtr< SimplePDF::PDF_Object >( ) )
386 return resource_;
389 typedef typeof theFontResourceMap_ MapType;
392 MapType::const_iterator i = theFontResourceMap_.find( fontName_ );
393 if( i != theFontResourceMap_.end( ) )
395 resource_ = i->second;
396 return resource_;
401 RefCountPtr< SimplePDF::PDF_Dictionary > dic;
402 resource_ = SimplePDF::indirect( dic, & Kernel::theIndirectObjectCount );
403 (*dic)[ "Type" ] = SimplePDF::newName( "Font" );
404 (*dic)[ "Subtype" ] = SimplePDF::newName( "Type1" );
405 (*dic)[ "Encoding" ] = SimplePDF::newName( "MacRomanEncoding" );
406 (*dic)[ "BaseFont" ] = SimplePDF::newName( fontName_.getPtr( ) ); // Here, it is crucial that this is really a built-in font!
408 return resource_;
411 RefCountPtr< const char >
412 Lang::Font::fontName( ) const
414 return fontName_;
417 bool
418 Lang::Font::outline( ) const
420 return outline_;
423 RefCountPtr< const FontMetrics::FontMetric >
424 Lang::Font::metrics( ) const
426 if( metrics_ != NullPtr< const FontMetrics::FontMetric >( ) )
428 return metrics_;
431 // Otherwise, we hope that we can find an Adobe Font Metrics file.
432 std::string filename = searchFontMetrics( fontName_ );
433 std::ifstream afmFile( filename.c_str( ) );
434 if( ! afmFile.is_open( ) )
436 std::ostringstream oss;
437 oss << "File has been located but couldn't be opened: " << filename ;
438 throw Exceptions::ExternalError( strrefdup( oss ) );
441 // I fiddle a little with the consts here...
442 FontMetrics::AFM * newMetrics = new FontMetrics::AFM;
443 metrics_ = RefCountPtr< const FontMetrics::FontMetric >( newMetrics );
445 AfmScanner scanner( newMetrics, & afmFile );
446 scanner.setTellQue( Interaction::fontMetricMessages ); // We want to know about things that are not recognized and this ignored.
447 if( Interaction::fontMetricDebug )
449 scanner.set_debug( 1 );
453 int status = scanner.yylex( );
454 if( status != 0 )
456 std::ostringstream oss;
457 oss << "Font metrics parser returned with non-zero status: " << status ;
458 throw Exceptions::InternalError( strrefdup( oss ) );
461 catch( const char * ball )
463 std::ostringstream oss;
464 oss << "Font metrics parser failed with message: " << ball ;
465 throw Exceptions::InternalError( strrefdup( oss ) );
467 catch( const RefCountPtr< const char > ball )
469 std::ostringstream oss;
470 oss << "Font metrics parser failed with message: " << ball ;
471 throw Exceptions::InternalError( strrefdup( oss ) );
474 return metrics_;
477 void
478 Lang::Font::show( std::ostream & os ) const
480 os << "< font: " << fontName_ << " >" ;
483 void
484 Lang::Font::gcMark( Kernel::GCMarkedSet & marked )
487 RefCountPtr< const Lang::Class > Lang::Font::TypeID( new Lang::SystemFinalClass( strrefdup( "Font" ), Font_register_methods ) );
488 TYPEINFOIMPL( Font );
491 Lang::PDFStandardFont::PDFStandardFont( const RefCountPtr< const char > builtInFontName )
492 : Lang::Font( builtInFontName )
495 Lang::PDFStandardFont::~PDFStandardFont( )
498 void
499 Lang::PDFStandardFont::encode( const char ** text_UTF8, size_t * in_avail, char ** dst_PDF, size_t * out_avail ) const
501 encode_MacRoman( text_UTF8, in_avail, dst_PDF, out_avail );
504 void
505 Lang::PDFStandardFont::encode( Kernel::UnicodeCodePoint c, char ** dst_PDF, size_t * out_avail ) const
507 encode_MacRoman( c, dst_PDF, out_avail );
510 void
511 Lang::PDFStandardFont::encode_MacRoman( const char ** text_UTF8, size_t * in_avail, char ** dst_PDF, size_t * out_avail )
513 iconv_t converter = Helpers::requireUTF8ToMacRomanConverter( );
514 // The ICONV_CAST macro is defined in config.h.
515 size_t count = iconv( converter,
516 ICONV_CAST( text_UTF8 ), in_avail,
517 dst_PDF, out_avail );
518 if( count == (size_t)(-1) )
520 if( errno == EILSEQ )
522 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)." );
524 else if( errno == EINVAL )
526 throw Exceptions::MiscellaneousRequirement( "It is suspected that showed text ended with an incomplete multibyte character." );
528 else if( errno == E2BIG )
530 throw Exceptions::InternalError( "The buffer allocated for UTF-8 to PDF show-text string conversion was too small." );
532 else
534 std::ostringstream msg;
535 msg << "iconv failed with an unrecognized error code: " << errno ;
536 throw Exceptions::InternalError( strrefdup( msg ) );
541 void
542 Lang::PDFStandardFont::encode_MacRoman( Kernel::UnicodeCodePoint c, char ** dst_PDF, size_t * out_avail )
544 if( *out_avail < 1 )
546 throw Exceptions::InternalError( "The buffer allocated conversion of a single UCS-4 to MacRoman was too small." );
548 **dst_PDF = c.get_MacRoman( );
549 ++*dst_PDF;
550 --*out_avail;
554 Lang::Type3Font::Type3Font( const RefCountPtr< const char > fontName, RefCountPtr< SimplePDF::PDF_Object > & resource, RefCountPtr< const FontMetrics::FontMetric > metrics )
555 : Lang::Font( fontName, false, resource, metrics )
558 Lang::Type3Font::~Type3Font( )
561 void
562 Lang::Type3Font::encode( const char ** text_UTF8, size_t * in_avail, char ** dst_PDF, size_t * out_avail ) const
564 Lang::PDFStandardFont::encode_MacRoman( text_UTF8, in_avail, dst_PDF, out_avail );
567 void
568 Lang::Type3Font::encode( Kernel::UnicodeCodePoint c, char ** dst_PDF, size_t * out_avail ) const
570 Lang::PDFStandardFont::encode_MacRoman( c, dst_PDF, out_avail );
575 #ifdef HAVE_FT2
577 Lang::FreeTypeFont::FreeTypeFont( FT_Face face, const RefCountPtr< const char > fontName, bool outline, RefCountPtr< SimplePDF::PDF_Object > & resource, RefCountPtr< const FontMetrics::FontMetric > metrics, RefCountPtr< SimplePDF::PDF_ToUnicode::CharSet > & usedChars )
578 : Lang::Font( fontName, outline, resource, metrics ),
579 face_( face ),
580 usedChars_( usedChars )
583 Lang::FreeTypeFont::~FreeTypeFont( )
586 void
587 Lang::FreeTypeFont::encode( const char ** text_UTF8, size_t * in_avail, char ** dst_PDF, size_t * out_avail ) const
589 iconv_t converter = Helpers::requireUTF8ToUCS4Converter( );
591 size_t bufSize = 4 * *in_avail;
592 char * buf = new char[ bufSize ];
593 char * outbuf = buf;
594 DeleteOnExit< char > bufDeleter( buf );
596 // The ICONV_CAST macro is defined in config.h.
597 size_t count = iconv( converter,
598 ICONV_CAST( text_UTF8 ), in_avail,
599 & outbuf, & bufSize );
600 if( count == (size_t)(-1) )
602 if( errno == EILSEQ )
604 throw Exceptions::MiscellaneousRequirement( "Failed to convert from UTF-8 to UCS-4 code points." );
606 else if( errno == EINVAL )
608 throw Exceptions::MiscellaneousRequirement( "It is suspected that showed text ended with an incomplete multibyte character." );
610 else if( errno == E2BIG )
612 throw Exceptions::InternalError( "The temporary UCS-4 buffer allocated for UTF-8 to PDF show-text string conversion was too small." );
614 else
616 std::ostringstream msg;
617 msg << "iconv failed with an unrecognized error code: " << errno ;
618 throw Exceptions::InternalError( strrefdup( msg ) );
621 /* The number of characters equals ( outbuf - buf ) / 4, so the memory needed to store these equals
622 * 2 * ( outbuf - buf ) / 4 = ( outbuf - buf ) / 2
624 if( static_cast< ptrdiff_t >( *out_avail ) < ( outbuf - buf ) / 2 )
626 throw Exceptions::InternalError( "The buffer allocated for UTF-8 to PDF show-text string conversion was too small." );
628 *out_avail -= ( outbuf - buf ) / 2;
629 for( const char * src = buf; src != outbuf; src += 4)
631 Kernel::UnicodeCodePoint code;
632 code.decode_UCS4( src );
633 usedChars_->insert( usedChars_->begin( ), code );
634 FT_UInt index = FT_Get_Char_Index( face_, code.get_UCS4( ) );
635 if( index == 0 )
637 std::ostringstream msg;
638 msg << "Missing glyph U+" << std::hex << code.get_UCS4( ) << "." ;
639 WARN_OR_THROW( Exceptions::FontProblem( this->PostScript_name( ), strrefdup( msg ), true ) );
641 if( index > 0xFFFF )
643 throw Exceptions::InternalError( "PDF text encoding: Glyph index is out of the two byte code range." );
645 /* Remember that the endianness is unknown! Hence, don't */
646 // *reinterpret_cast< uint16_t * >( *dst_PDF ) = index;
647 // *dst_PDF += 2;
648 /* ... but do */
649 **dst_PDF = index / 0x100;
650 ++*dst_PDF;
651 **dst_PDF = index % 0x100;
652 ++*dst_PDF;
656 void
657 Lang::FreeTypeFont::encode( Kernel::UnicodeCodePoint c, char ** dst_PDF, size_t * out_avail ) const
659 if( *out_avail < 2 )
661 throw Exceptions::InternalError( "The buffer allocated conversion of one UCS-4 code point to PDF show-text encoding was too small." );
663 FT_UInt index = FT_Get_Char_Index( face_, c.get_UCS4( ) );
664 if( index == 0 )
666 std::ostringstream msg;
667 msg << "Missing glyph U+" << std::hex << c.get_UCS4( ) << ".";
668 WARN_OR_THROW( Exceptions::FontProblem( this->PostScript_name( ), strrefdup( msg ), true ) );
670 if( index > 0xFFFF )
672 throw Exceptions::InternalError( "PDF text encoding: Glyph index is out of the two byte code range." );
674 /* Remember that the endianness is unknown! Hence, don't */
675 // *reinterpret_cast< uint16_t * >( *dst_PDF ) = index;
676 // *dst_PDF += 2;
677 /* ... but do */
678 **dst_PDF = index / 0x100;
679 ++*dst_PDF;
680 **dst_PDF = index % 0x100;
681 ++*dst_PDF;
682 *out_avail -= 2;
685 RefCountPtr< const Lang::String >
686 Lang::FreeTypeFont::family_name( ) const
688 return RefCountPtr< const Lang::String >( new Lang::String( face_->family_name, true ) );
691 RefCountPtr< const Lang::String >
692 Lang::FreeTypeFont::style_name( ) const
694 return RefCountPtr< const Lang::String >( new Lang::String( face_->style_name, true ) );
697 RefCountPtr< const Lang::Symbol >
698 Lang::FreeTypeFont::PostScript_name( ) const
700 const char * PostScriptName = FT_Get_Postscript_Name( face_ );
701 if( PostScriptName == 0 )
703 throw Exceptions::MiscellaneousRequirement( "The FreeType font does not provide a PostScript name." );
705 /* It is assumed that the created symbol is not used after the face_ is destroyed! */
706 return RefCountPtr< const Lang::Symbol >( new Lang::Symbol( PostScriptName ) );
709 namespace Shapes
711 namespace Helpers
713 class GetGlyphState
715 public:
716 Lang::MultiPath2D * paths_;
717 Lang::ElementaryPath2D * contour_;
718 Concrete::PathPoint2D * last_;
719 Concrete::Length scaled_font_unit_;
721 void close( )
723 if( contour_ != 0 )
725 /* We should eliminate the duplicated point where the path begins and ends. */
726 contour_->pop_back( );
727 if( last_->rear_ != last_->mid_ )
729 contour_->front( )->rear_ = last_->rear_;
730 last_->rear_ = last_->mid_; /* This gives away the ownership of last_->rear_. */
732 delete last_;
733 last_ = 0;
734 contour_->close( );
735 contour_ = 0;
738 int move_to( const FT_Vector & to )
740 close( );
741 contour_ = new Lang::ElementaryPath2D( );
742 paths_->push_back( RefCountPtr< const Lang::Path2D >( contour_ ) );
743 last_ = new Concrete::PathPoint2D( new Concrete::Coords2D( to.x * scaled_font_unit_, to.y * scaled_font_unit_ ) );
744 contour_->push_back( last_ );
745 return 0;
747 int line_to( const FT_Vector & to )
749 last_ = new Concrete::PathPoint2D( new Concrete::Coords2D( to.x * scaled_font_unit_, to.y * scaled_font_unit_ ) );
750 contour_->push_back( last_ );
751 return 0;
753 int conic_to( const FT_Vector & control, const FT_Vector & to )
755 Concrete::Coords2D c( control.x * scaled_font_unit_, control.y * scaled_font_unit_ );
756 last_->front_ = new Concrete::Coords2D( (2./3) * c + (1./3) * *last_->mid_ );
757 last_ = new Concrete::PathPoint2D( new Concrete::Coords2D( to.x * scaled_font_unit_, to.y * scaled_font_unit_ ) );
758 last_->rear_ = new Concrete::Coords2D( (2./3) * c + (1./3) * *last_->mid_ );
759 contour_->push_back( last_ );
760 return 0;
762 int cubic_to( const FT_Vector & control1, const FT_Vector & control2, const FT_Vector & to )
764 last_->front_ = new Concrete::Coords2D( control1.x * scaled_font_unit_, control1.y * scaled_font_unit_ );
765 last_ = new Concrete::PathPoint2D( new Concrete::Coords2D( to.x * scaled_font_unit_, to.y * scaled_font_unit_ ) );
766 last_->rear_ = new Concrete::Coords2D( control2.x * scaled_font_unit_, control2.y * scaled_font_unit_ );
767 contour_->push_back( last_ );
768 return 0;
771 GetGlyphState( Lang::MultiPath2D * paths, Concrete::Length scaled_font_unit )
772 : paths_( paths ),
773 contour_( 0 ),
774 last_( 0 ),
775 scaled_font_unit_( scaled_font_unit )
777 ~GetGlyphState( )
779 close( );
782 int getGlyph_move_to( const FT_Vector * to, void * stateUntyped )
784 return reinterpret_cast< GetGlyphState * >( stateUntyped )->move_to( *to );
786 int getGlyph_line_to( const FT_Vector * to, void * stateUntyped )
788 return reinterpret_cast< GetGlyphState * >( stateUntyped )->line_to( *to );
790 int getGlyph_conic_to( const FT_Vector * control, const FT_Vector * to, void * stateUntyped )
792 return reinterpret_cast< GetGlyphState * >( stateUntyped )->conic_to( *control, *to );
794 int getGlyph_cubic_to( const FT_Vector * control1, const FT_Vector * control2, const FT_Vector * to, void * stateUntyped )
796 return reinterpret_cast< GetGlyphState * >( stateUntyped )->cubic_to( *control1, *control2, *to );
799 class ShipoutGlyphState
801 public:
802 bool open_;
803 Concrete::Coords2D last_;
804 std::ostream & os_;
805 Concrete::Length scaled_font_unit_;
806 const Lang::Transform2D & tf_;
808 void close( )
810 if( open_ )
812 os_ << "h " ;
813 open_ = false;
816 int move_to( const FT_Vector & to )
818 close( );
819 last_ = Concrete::Coords2D( to.x * scaled_font_unit_, to.y * scaled_font_unit_ ).transformed( tf_ );
820 os_ << last_ << " m " ;
821 open_ = true;
822 return 0;
824 int line_to( const FT_Vector & to )
826 last_ = Concrete::Coords2D( to.x * scaled_font_unit_, to.y * scaled_font_unit_ ).transformed( tf_ );
827 os_ << last_ << " l " ;
828 return 0;
830 int conic_to( const FT_Vector & control, const FT_Vector & to )
832 Concrete::Coords2D c = Concrete::Coords2D( control.x * scaled_font_unit_, control.y * scaled_font_unit_ ).transformed( tf_ );
833 Concrete::Coords2D control1 = (2./3) * c + (1./3) * last_;
834 last_ = Concrete::Coords2D( to.x * scaled_font_unit_, to.y * scaled_font_unit_ ).transformed( tf_ );
835 Concrete::Coords2D control2 = (2./3) * c + (1./3) * last_;
836 os_ << control1 << " " << control2 << " " << last_ << " c " ;
837 return 0;
839 int cubic_to( const FT_Vector & control1, const FT_Vector & control2, const FT_Vector & to )
841 last_ = Concrete::Coords2D( to.x * scaled_font_unit_, to.y * scaled_font_unit_ ).transformed( tf_ );
842 os_ << Concrete::Coords2D( control1.x * scaled_font_unit_, control1.y * scaled_font_unit_ ).transformed( tf_ )
843 << " " << Concrete::Coords2D( control2.x * scaled_font_unit_, control2.y * scaled_font_unit_ ).transformed( tf_ )
844 << " " << last_
845 << " c " ;
846 return 0;
849 ShipoutGlyphState( std::ostream & os,
850 Concrete::Length scaled_font_unit,
851 const Lang::Transform2D & tf )
852 : open_( false ),
853 last_( 0, 0 ),
854 os_( os ),
855 scaled_font_unit_( scaled_font_unit ),
856 tf_( tf )
858 ~ShipoutGlyphState( )
860 close( );
863 int shipoutGlyph_move_to( const FT_Vector * to, void * stateUntyped )
865 return reinterpret_cast< ShipoutGlyphState * >( stateUntyped )->move_to( *to );
867 int shipoutGlyph_line_to( const FT_Vector * to, void * stateUntyped )
869 return reinterpret_cast< ShipoutGlyphState * >( stateUntyped )->line_to( *to );
871 int shipoutGlyph_conic_to( const FT_Vector * control, const FT_Vector * to, void * stateUntyped )
873 return reinterpret_cast< ShipoutGlyphState * >( stateUntyped )->conic_to( *control, *to );
875 int shipoutGlyph_cubic_to( const FT_Vector * control1, const FT_Vector * control2, const FT_Vector * to, void * stateUntyped )
877 return reinterpret_cast< ShipoutGlyphState * >( stateUntyped )->cubic_to( *control1, *control2, *to );
882 const FT_Outline &
883 Lang::FreeTypeFont::getGlyphOutline( Kernel::UnicodeCodePoint c ) const
885 FT_UInt index = FT_Get_Char_Index( face_, c.get_UCS4( ) );
886 if( index == 0 )
888 const size_t bufsize = 10;
889 char buf[ bufsize ];
890 char * tmp = buf;
891 size_t outavail = bufsize - 1;
892 c.encode_UTF8( & tmp, & outavail );
893 *tmp = '\0';
894 std::ostringstream msg;
895 msg << "The font " << fontName_ << " does not cover the character `" << buf << "´ (code point " << c.get_UCS4( ) << ")." ;
896 throw Exceptions::OutOfRange( strrefdup( msg ) );
898 FT_Error error = FT_Load_Glyph( face_, index, FT_LOAD_NO_SCALE );
899 if( error != 0 )
901 std::ostringstream msg;
902 msg << "Unable to load glyph in FreeType, at code point " << c.get_UCS4( ) ;
903 throw Shapes::Exceptions::OutOfRange( strrefdup( msg ) );
905 if( face_->glyph->format != FT_GLYPH_FORMAT_OUTLINE )
907 const size_t bufsize = 10;
908 char buf[ bufsize ];
909 char * tmp = buf;
910 size_t outavail = bufsize - 1;
911 c.encode_UTF8( & tmp, & outavail );
912 *tmp = '\0';
913 std::ostringstream msg;
914 msg << "Unsupported glyph format in the font " << fontName_ << ", character `" << buf << "´ (code point " << c.get_UCS4( ) << ")." ;
915 throw Exceptions::MiscellaneousRequirement( strrefdup( msg ) );
918 return face_->glyph->outline;
921 RefCountPtr< const Lang::Value >
922 Lang::FreeTypeFont::getGlyph( Kernel::UnicodeCodePoint c, Concrete::Length size ) const
924 const FT_Outline & ol = getGlyphOutline( c );
926 Helpers::FontMethod_glyph_resultFactory.set
927 ( "odd?", ( ( ol.flags & FT_OUTLINE_REVERSE_FILL ) == 0 ) ? Kernel::THE_FALSE_VARIABLE : Kernel::THE_TRUE_VARIABLE );
928 Concrete::Length font_unit_scaled = size / static_cast< double >( face_->units_per_EM );
929 Lang::MultiPath2D * pathsPtr = new Lang::MultiPath2D( );
930 RefCountPtr< const Lang::MultiPath2D > paths( pathsPtr );
932 FT_Outline_Funcs emitters;
933 emitters.move_to = Helpers::getGlyph_move_to;
934 emitters.line_to = Helpers::getGlyph_line_to;
935 emitters.conic_to = Helpers::getGlyph_conic_to;
936 emitters.cubic_to = Helpers::getGlyph_cubic_to;
937 emitters.shift = 0;
938 emitters.delta = 0;
939 Helpers::GetGlyphState state( pathsPtr, font_unit_scaled );
940 FT_Error error = FT_Outline_Decompose( & face_->glyph->outline, & emitters, reinterpret_cast< void * >( & state ) );
941 if( error != 0 )
943 std::ostringstream msg;
944 msg << "FreeType error, FT_Outline_Decompose returned with error: " << Kernel::FreeTypeErrorMessage( error ) ;
945 throw Exceptions::ExternalError( strrefdup( msg ) );
948 Helpers::FontMethod_glyph_resultFactory.set( "paths", Kernel::VariableHandle( new Kernel::Variable( paths ) ) );
950 Helpers::FontMethod_glyph_resultFactory.set( "advance", Helpers::newValHandle( new Lang::Length( face_->glyph->metrics.horiAdvance * font_unit_scaled ) ) );
952 return Helpers::FontMethod_glyph_resultFactory.build( );
955 void
956 Lang::FreeTypeFont::shipout_glyph( Kernel::UnicodeCodePoint c, Concrete::Length size, const Lang::Transform2D & tf, std::ostream & os, bool * dstIsOdd, Concrete::Length * dstAdvance ) const
958 const FT_Outline & ol = getGlyphOutline( c );
960 Concrete::Length font_unit_scaled = size / static_cast< double >( face_->units_per_EM );
962 *dstIsOdd = ( ol.flags & FT_OUTLINE_REVERSE_FILL ) != 0;
963 *dstAdvance = Concrete::Length( face_->glyph->metrics.horiAdvance * font_unit_scaled ); /* Here we use that the correct glyph is in the glyph slot. */
965 FT_Outline_Funcs emitters;
966 emitters.move_to = Helpers::shipoutGlyph_move_to;
967 emitters.line_to = Helpers::shipoutGlyph_line_to;
968 emitters.conic_to = Helpers::shipoutGlyph_conic_to;
969 emitters.cubic_to = Helpers::shipoutGlyph_cubic_to;
970 emitters.shift = 0;
971 emitters.delta = 0;
972 Helpers::ShipoutGlyphState state( os, font_unit_scaled, tf );
973 FT_Error error = FT_Outline_Decompose( const_cast< FT_Outline * >( & ol ), & emitters, reinterpret_cast< void * >( & state ) );
974 if( error != 0 )
976 std::ostringstream msg;
977 msg << "FreeType error, FT_Outline_Decompose returned with error: " << Kernel::FreeTypeErrorMessage( error ) ;
978 throw Exceptions::ExternalError( strrefdup( msg ) );
982 #endif /* End of #ifdef HAVE_FT2. */
984 Lang::TextOperation::TextOperation( )
987 Lang::TextOperation::~TextOperation( )
990 RefCountPtr< const Lang::Class > Lang::TextOperation::TypeID( new Lang::SystemFinalClass( strrefdup( "TextOperation" ) ) );
991 TYPEINFOIMPL( TextOperation );
994 Lang::KernedText::KernedText( const RefCountPtr< const Kernel::TextState > & textState, const RefCountPtr< const Kernel::GraphicsState > & metaState )
995 : textState_( textState ), metaState_( metaState ), maxLength_( 0 )
998 Lang::KernedText::KernedText( const RefCountPtr< const Kernel::TextState > & textState, const RefCountPtr< const Kernel::GraphicsState > & metaState, const RefCountPtr< const Lang::String > & str)
999 : textState_( textState ), metaState_( metaState ), maxLength_( 0 )
1001 pushString( str );
1004 Lang::KernedText::~KernedText( )
1007 Kernel::VariableHandle
1008 Lang::KernedText::getField( const char * fieldID, const RefCountPtr< const Lang::Value > & selfRef ) const
1010 if( strcmp( fieldID, "list" ) == 0 )
1012 return Kernel::VariableHandle( new Kernel::Variable( makeList( ) ) );
1014 throw Exceptions::NonExistentMember( getTypeName( ), fieldID );
1017 RefCountPtr< const Lang::SingleList >
1018 Lang::KernedText::makeList( ) const
1020 /* The list is first computed in a bidirectional list, and then we construct the stateless cons list.
1023 /* I'm lazy today, so i use a cons pair to group the horizontal step with the glyph. The better solution
1024 * would be to use a structure with nicely named fields...
1027 std::list< RefCountPtr< const Lang::Value > > revlist;
1029 iconv_t converter = Helpers::requireUTF8ToUCS4Converter( );
1031 RefCountPtr< FontMetrics::WritingDirectionMetrics > horizontalMetrics = textState_->font_->metrics( )->horizontalMetrics_;
1032 if( horizontalMetrics == NullPtr< FontMetrics::WritingDirectionMetrics >( ) )
1034 throw Exceptions::FontMetricsError( textState_->font_->fontName( ), strrefdup( "No horizontal metrics defined." ) );
1036 const FontMetrics::CharacterMetrics * defaultCharMetrics = horizontalMetrics->default_char( );
1038 Concrete::Length ySize = textState_->size_;
1039 Concrete::Length xSize = ySize * textState_->horizontalScaling_;
1040 Concrete::Length characterTrackKern = textState_->horizontalScaling_ * textState_->characterSpacingConcrete( );
1041 Concrete::Length wordTrackKern = textState_->horizontalScaling_ * textState_->wordSpacingConcrete( );
1043 Concrete::Length xpos = 0;
1045 size_t bufSize = 4 * maxLength_; // This will be enough if UCS4 coding is used, since this encoding uses only one byte per character.
1046 char * buf = new char[ bufSize ];
1047 DeleteOnExit< char > bufDeleter( buf );
1049 FontMetrics::CharacterMetrics tmpCharMetric( 0 );
1051 typedef typeof strings_ ListType;
1052 std::list< double >::const_iterator ki = kernings_.begin( );
1053 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
1055 if( *i != NullPtr< const Lang::String >( ) )
1057 const char * inbuf = (*i)->val_.getPtr( );
1058 if( strchr( inbuf, '\n' ) != 0 )
1060 throw Exceptions::MiscellaneousRequirement( "Newlines cannot be represented in the pos-character list." );
1062 char * outbuf = buf;
1063 size_t inbytesleft = strlen( inbuf );
1064 size_t outbytesleft = bufSize;
1065 // The ICONV_CAST macro is defined in config.h.
1066 size_t count = iconv( converter,
1067 ICONV_CAST( & inbuf ), & inbytesleft,
1068 & outbuf, & outbytesleft );
1069 if( count == (size_t)(-1) )
1071 if( errno == EILSEQ )
1073 throw Exceptions::MiscellaneousRequirement( "It is suspected that string data is not proper UFT-8, since conversion to UCS-4 failed." );
1075 else if( errno == EINVAL )
1077 throw Exceptions::MiscellaneousRequirement( "It is suspected that showed text ended with an incomplete multibyte character." );
1079 else if( errno == E2BIG )
1081 throw Exceptions::InternalError( "The buffer allocated for UTF-8 to UCS-4 conversion was too small." );
1083 else
1085 std::ostringstream msg;
1086 msg << "iconv failed with an unrecognized error code: " << errno ;
1087 throw Exceptions::InternalError( strrefdup( msg ) );
1090 for( const char * src = buf; src != outbuf; src += 4 )
1092 Kernel::UnicodeCodePoint code;
1093 code.decode_UCS4( src );
1094 if( code == Kernel::UnicodeCodePoint::SPACE ) /* test for space */
1096 // Observe textState_->wordSpacing_
1097 const FontMetrics::CharacterMetrics * charMetrics = horizontalMetrics->charByCode( code, & tmpCharMetric );
1098 xpos += xSize * charMetrics->horizontalCharWidthX_;
1099 xpos += characterTrackKern;
1100 xpos += wordTrackKern;
1102 else /* there should not be any newlines in the string, so we expect this to be a non-white character */
1104 // Observe textState_->characterSpacing_
1105 const FontMetrics::CharacterMetrics * charMetrics = horizontalMetrics->charByCode( code, & tmpCharMetric );
1106 if( Computation::fontMetricGuessIsError && charMetrics == defaultCharMetrics )
1108 std::ostringstream msg;
1109 msg << "Character at offset " << src - buf << " in \"" << buf << "\" was not found in font metrics (and according to your options, guessing is not OK)." ;
1110 throw Exceptions::FontMetricsError( textState_->font_->fontName( ), strrefdup( msg ) );
1112 revlist.push_back( RefCountPtr< const Lang::Value >
1113 ( new Lang::ConsPair
1114 ( Helpers::newValHandle( new Lang::Length( xpos ) ),
1115 Helpers::newValHandle( new Lang::KernedText( textState_, metaState_, oneUCS4ToUTF8( src ) ) ) ) ) );
1116 /* Although not as efficient, it becomes easier to use the list if the distance is
1117 * measured from the start of the text rather than from the previous character.
1119 // xpos = 0;
1120 xpos += xSize * charMetrics->horizontalCharWidthX_;
1121 xpos += characterTrackKern;
1125 else
1127 if( ki == kernings_.end( ) )
1129 throw Exceptions::InternalError( "Short of kerning values in KernedText::measure." );
1131 xpos -= xSize * *ki;
1132 ++ki;
1135 if( ki != kernings_.end( ) )
1137 throw Exceptions::InternalError( "Too many kerning values in KernedText::writePDFVectorTo." );
1140 RefCountPtr< const Lang::SingleList > res = Lang::THE_CONS_NULL;
1141 while( ! revlist.empty( ) )
1143 res = RefCountPtr< const Lang::SingleList >( new Lang::SingleListPair( Kernel::VariableHandle( new Kernel::Variable( revlist.back( ) ) ),
1144 res ) );
1145 revlist.pop_back( );
1147 return res;
1150 RefCountPtr< const Lang::String >
1151 Lang::KernedText::oneUCS4ToUTF8( const char * c )
1153 iconv_t converter = Helpers::requireUCS4ToUTF8Converter( );
1155 const size_t BUF_SIZE = 9;
1156 char buf[ BUF_SIZE ];
1158 char charbuf[ 5 ];
1159 charbuf[0] = *c;
1160 ++c;
1161 charbuf[1] = *c;
1162 ++c;
1163 charbuf[2] = *c;
1164 ++c;
1165 charbuf[3] = *c;
1166 charbuf[4] = '\0';
1168 char * outbuf = buf;
1169 const char * inbuf = charbuf;
1170 size_t inbytesleft = 4;
1171 size_t outbytesleft = BUF_SIZE - 1;
1172 // The ICONV_CAST macro is defined in config.h.
1173 size_t count = iconv( converter,
1174 ICONV_CAST( & inbuf ), & inbytesleft,
1175 & outbuf, & outbytesleft );
1176 if( count == (size_t)(-1) )
1178 throw Exceptions::ExternalError( "Conversion of one UCS-4 character to UTF-8 failed." );
1180 *outbuf = '\0';
1181 return RefCountPtr< const Lang::String >( new Lang::String( strrefdup( buf ) ) );
1184 void
1185 Lang::KernedText::pushString( const RefCountPtr< const Lang::String > & str )
1187 strings_.push_back( str );
1188 maxLength_ = std::max( maxLength_, strlen( str->val_.getPtr( ) ) );
1191 void
1192 Lang::KernedText::pushKerning( double kerning )
1194 strings_.push_back( NullPtr< const Lang::String >( ) );
1195 kernings_.push_back( kerning );
1198 void
1199 Lang::KernedText::show( std::ostream & os ) const
1201 typedef typeof strings_ ListType;
1202 std::list< double >::const_iterator ki = kernings_.begin( );
1203 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
1205 if( *i != NullPtr< const Lang::String >( ) )
1207 os << "(" << (*i)->val_.getPtr( ) << ")" ;
1209 else
1211 if( ki == kernings_.end( ) )
1213 throw Exceptions::InternalError( "Short of kerning values in KernedText::show." );
1215 os << *ki ;
1216 ++ki;
1219 if( ki != kernings_.end( ) )
1221 throw Exceptions::InternalError( "Too many kerning values in KernedText::show." );
1226 void
1227 Lang::KernedText::shipout( std::ostream & os, Kernel::PageContentStates * pdfState, const Lang::Transform2D & tf, bool clip ) const
1229 pdfState->text_.synchAssertKnockout( os, textState_.getPtr( ), pdfState->resources_.getPtr( ), clip );
1230 switch( textState_->mode_ )
1232 case Lang::TextRenderingMode::FILL:
1233 case Lang::TextRenderingMode::FILLCLIP:
1234 pdfState->graphics_.synchForNonStroke( os, metaState_.getPtr( ), pdfState->resources_.getPtr( ) );
1235 break;
1236 case Lang::TextRenderingMode::STROKE:
1237 case Lang::TextRenderingMode::STROKECLIP:
1238 pdfState->graphics_.synchForStroke( os, metaState_.getPtr( ), pdfState->resources_.getPtr( ) );
1239 break;
1240 case Lang::TextRenderingMode::FILLSTROKE:
1241 case Lang::TextRenderingMode::FILLSTROKECLIP:
1242 pdfState->graphics_.synchForStroke( os, metaState_.getPtr( ), pdfState->resources_.getPtr( ) );
1243 pdfState->graphics_.synchForNonStroke( os, metaState_.getPtr( ), pdfState->resources_.getPtr( ) );
1244 break;
1245 case Lang::TextRenderingMode::INVISIBLE:
1246 case Lang::TextRenderingMode::CLIP:
1247 break;
1248 case Lang::TextRenderingMode::UNDEFINED:
1249 pdfState->graphics_.synchForStroke( os, metaState_.getPtr( ), pdfState->resources_.getPtr( ) );
1250 pdfState->graphics_.synchForNonStroke( os, metaState_.getPtr( ), pdfState->resources_.getPtr( ) );
1251 default:
1252 throw Exceptions::InternalError( "KernedText::writePDFVectorTo: Text rendering mode out of range." );
1256 size_t bufSize = 4 * maxLength_; // Even with UTF16BE encoding, no character should use more than 4 bytes.
1257 // The extra one byte will be used to terminate the string.
1258 char * buf = new char[ bufSize ];
1259 DeleteOnExit< char > bufDeleter( buf );
1261 typedef typeof strings_ ListType;
1262 std::list< double >::const_iterator ki = kernings_.begin( );
1263 os << "[ " ;
1264 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
1266 if( *i != NullPtr< const Lang::String >( ) )
1268 os << "(" ;
1269 const char * line = (*i)->val_.getPtr( );
1270 while( true )
1272 const char * nextLine = strchr( line, '\n' ); /* Search for line breaks before we convert from UTF8. */
1273 const char * inbuf = line;
1274 char * outbuf = buf;
1275 size_t inbytesleft = ( nextLine != 0 ) ? ( nextLine - line ) : strlen( line );
1276 size_t outbytesleft = bufSize;
1277 textState_->font_->encode( & inbuf, & inbytesleft,
1278 & outbuf, & outbytesleft );
1279 const char * end = buf + ( bufSize - outbytesleft );
1280 for( const char * src = buf; src != end; ++src )
1282 switch( *src )
1284 case '(':
1285 os << "\\(" ;
1286 break;
1287 case ')':
1288 os << "\\)" ;
1289 break;
1290 case '\\':
1291 os << "\\\\" ;
1292 break;
1293 default:
1294 os << *src ;
1297 if( nextLine == 0 )
1299 break;
1301 os << ")] TJ T* [(" ;
1302 line = nextLine;
1303 ++line; /* Skip the \n character */
1305 os << ") " ;
1307 else
1309 if( ki == kernings_.end( ) )
1311 throw Exceptions::InternalError( "Short of kerning values in KernedText::writePDFVectorTo." );
1313 os << 1000 * *ki << " " ;
1314 ++ki;
1317 if( ki != kernings_.end( ) )
1319 throw Exceptions::InternalError( "Too many kerning values in KernedText::writePDFVectorTo." );
1322 os << "] TJ " << std::endl ;
1325 void
1326 Lang::KernedText::shipout_outlined( std::ostream & os, Kernel::PageContentStates * pdfState, const Lang::Transform2D & tf, Lang::Transform2D * textMatrix, Lang::Transform2D * lineMatrix, const SimplePDF::PDF_Name * clipContent ) const
1328 #ifndef HAVE_FT2
1329 throw Exceptions::BuildRequirement( Interaction::BUILD_REQ_FREETYPE, "(outlined fonts)", Ast::THE_UNKNOWN_LOCATION );
1330 #else
1331 iconv_t converter = Helpers::requireUTF8ToUCS4Converter( );
1333 RefCountPtr< const Lang::FreeTypeFont > font = textState_->font_.down_cast< const Lang::FreeTypeFont >( );
1334 if( font == NullPtr< typeof *font >( ) )
1336 throw Exceptions::MiscellaneousRequirement( "Text masking is only supported for fonts accessed through FreeType." );
1339 RefCountPtr< FontMetrics::WritingDirectionMetrics > horizontalMetrics = font->metrics( )->horizontalMetrics_;
1340 if( horizontalMetrics == NullPtr< FontMetrics::WritingDirectionMetrics >( ) )
1342 throw Exceptions::FontMetricsError( font->fontName( ), strrefdup( "No horizontal metrics defined." ) );
1345 Concrete::Length ySize = textState_->size_;
1346 Concrete::Length xSize = ySize * textState_->horizontalScaling_;
1347 Concrete::Length characterTrackKern = textState_->horizontalScaling_ * textState_->characterSpacingConcrete( );
1348 Concrete::Length wordTrackKern = textState_->horizontalScaling_ * textState_->wordSpacingConcrete( );
1350 size_t bufSize = 4 * maxLength_;
1351 char * buf = new char[ bufSize ];
1352 DeleteOnExit< char > bufDeleter( buf );
1354 Concrete::Length rise = textState_->riseConcrete( );
1355 FontMetrics::CharacterMetrics tmpCharMetric( 0 );
1357 if( textState_->mode_ == Lang::TextRenderingMode::STROKE ||
1358 textState_->mode_ == Lang::TextRenderingMode::FILLSTROKE )
1360 pdfState->graphics_.synchForStroke( os, metaState_.getPtr( ), pdfState->resources_.getPtr( ) );
1362 if( textState_->mode_ == Lang::TextRenderingMode::FILL ||
1363 textState_->mode_ == Lang::TextRenderingMode::FILLSTROKE )
1365 pdfState->graphics_.synchForNonStroke( os, metaState_.getPtr( ), pdfState->resources_.getPtr( ) );
1368 typedef typeof strings_ ListType;
1369 std::list< double >::const_iterator ki = kernings_.begin( );
1370 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
1372 if( *i != NullPtr< const Lang::String >( ) )
1374 const char * inbuf = (*i)->val_.getPtr( );
1375 char * outbuf = buf;
1376 size_t inbytesleft = strlen( inbuf );
1377 size_t outbytesleft = bufSize;
1378 // The ICONV_CAST macro is defined in config.h.
1379 size_t count = iconv( converter,
1380 ICONV_CAST( & inbuf ), & inbytesleft,
1381 & outbuf, & outbytesleft );
1382 if( count == (size_t)(-1) )
1384 if( errno == EILSEQ )
1386 throw Exceptions::MiscellaneousRequirement( "Not proper UFT-8? Failed to convert to UCS-4 code points." );
1388 else if( errno == EINVAL )
1390 throw Exceptions::MiscellaneousRequirement( "It is suspected that showed text ended with an incomplete multibyte character." );
1392 else if( errno == E2BIG )
1394 throw Exceptions::InternalError( "The buffer allocated for UTF-8 to UCS-4 conversion was too small." );
1396 else
1398 std::ostringstream msg;
1399 msg << "iconv failed with an unrecognized error code: " << errno ;
1400 throw Exceptions::InternalError( strrefdup( msg ) );
1403 for( const char * src = buf; src != outbuf; src += 4 )
1405 Kernel::UnicodeCodePoint code;
1406 code.decode_UCS4( src );
1407 if( code == Kernel::UnicodeCodePoint::SPACE )
1409 // Observe textState_->wordSpacing_
1410 // In addition, it seems reasonable to not let the space character affect the bounding box.
1411 const FontMetrics::CharacterMetrics * charMetrics = horizontalMetrics->charByCode( code, & tmpCharMetric );
1412 textMatrix->prependXShift( xSize * charMetrics->horizontalCharWidthX_ + characterTrackKern + wordTrackKern );
1414 else if( code == Kernel::UnicodeCodePoint::NEWLINE )
1416 lineMatrix->prependShift( Concrete::Coords2D( 0, - textState_->leadingConcrete( ) ) );
1417 textMatrix->replaceBy( *lineMatrix );
1419 else
1421 // Observe textState_->characterSpacing_
1422 bool isOdd;
1423 Concrete::Length advance;
1424 Lang::Transform2D tmpTf( tf, *textMatrix );
1425 tmpTf.prependYShift( rise );
1426 tmpTf.prependXScale( textState_->horizontalScaling_ );
1427 if( clipContent == 0 )
1429 /* The graphics state was synched above. */
1430 font->shipout_glyph( code, ySize, tmpTf, os, & isOdd, & advance );
1431 switch( textState_->mode_ )
1433 case Lang::TextRenderingMode::FILL:
1434 os << ( isOdd ? "f*" : "f" ) << std::endl ;
1435 break;
1436 case Lang::TextRenderingMode::STROKE:
1437 os << "s" << std::endl ;
1438 break;
1439 case Lang::TextRenderingMode::FILLSTROKE:
1440 os << ( isOdd ? "B*" : "B" ) << std::endl ;
1441 break;
1442 case Lang::TextRenderingMode::INVISIBLE:
1443 os << "n" << std::endl ;
1444 break;
1445 case Lang::TextRenderingMode::FILLCLIP:
1446 case Lang::TextRenderingMode::STROKECLIP:
1447 case Lang::TextRenderingMode::FILLSTROKECLIP:
1448 case Lang::TextRenderingMode::CLIP:
1449 throw Exceptions::InternalError( "The clipping text rendering modes are not supposed to be used." );
1450 case Lang::TextRenderingMode::UNDEFINED:
1451 throw Exceptions::InternalError( "Undefined text rendering mode during shipout of outlined text." );
1454 else
1456 Kernel::Auto_qQ auto_qQ( & pdfState->graphics_, & pdfState->text_, os );
1457 font->shipout_glyph( code, ySize, tmpTf, os, & isOdd, & advance );
1458 os << ( isOdd ? "W* " : "W " ) ;
1459 switch( textState_->mode_ )
1461 case Lang::TextRenderingMode::FILL:
1462 os << ( isOdd ? "f*" : "f" ) ;
1463 break;
1464 case Lang::TextRenderingMode::STROKE:
1465 os << "s" ;
1466 break;
1467 case Lang::TextRenderingMode::FILLSTROKE:
1468 os << ( isOdd ? "B*" : "B" ) ;
1469 break;
1470 case Lang::TextRenderingMode::INVISIBLE:
1471 os << "n" ;
1472 break;
1473 case Lang::TextRenderingMode::FILLCLIP:
1474 case Lang::TextRenderingMode::STROKECLIP:
1475 case Lang::TextRenderingMode::FILLSTROKECLIP:
1476 case Lang::TextRenderingMode::CLIP:
1477 throw Exceptions::InternalError( "The clipping text rendering modes are not supposed to be used." );
1478 case Lang::TextRenderingMode::UNDEFINED:
1479 throw Exceptions::InternalError( "Undefined text rendering mode during shipout of outlined text." );
1481 os << " " << *clipContent << " Do" << std::endl ;
1484 textMatrix->prependXShift( advance * textState_->horizontalScaling_ + characterTrackKern );
1488 else
1490 if( ki == kernings_.end( ) )
1492 throw Exceptions::InternalError( "Short of kerning values in KernedText::shipout_outlined." );
1494 textMatrix->prependXShift( - xSize * *ki );
1495 ++ki;
1498 if( ki != kernings_.end( ) )
1500 throw Exceptions::InternalError( "Too many kerning values in KernedText::shipout_outlined." );
1502 #endif /* End of #ifndef HAVE_FT2 / #else. */
1505 void
1506 Lang::KernedText::measure( Lang::Transform2D * textMatrix, Lang::Transform2D * textLineMatrix, Concrete::Length * xmin, Concrete::Length * ymin, Concrete::Length * xmax, Concrete::Length * ymax ) const
1508 iconv_t converter = Helpers::requireUTF8ToUCS4Converter( );
1510 RefCountPtr< FontMetrics::WritingDirectionMetrics > horizontalMetrics = textState_->font_->metrics( )->horizontalMetrics_;
1511 if( horizontalMetrics == NullPtr< FontMetrics::WritingDirectionMetrics >( ) )
1513 throw Exceptions::FontMetricsError( textState_->font_->fontName( ), strrefdup( "No horizontal metrics defined." ) );
1515 const FontMetrics::CharacterMetrics * defaultCharMetrics = horizontalMetrics->default_char( );
1517 Concrete::Length ySize = textState_->size_;
1518 Concrete::Length xSize = ySize * textState_->horizontalScaling_;
1519 Concrete::Length characterTrackKern = textState_->horizontalScaling_ * textState_->characterSpacingConcrete( );
1520 Concrete::Length wordTrackKern = textState_->horizontalScaling_ * textState_->wordSpacingConcrete( );
1522 size_t bufSize = 4 * maxLength_;
1523 char * buf = new char[ bufSize ];
1524 DeleteOnExit< char > bufDeleter( buf );
1526 FontMetrics::CharacterMetrics tmpCharMetric( 0 );
1528 if( textLineMatrix->isTranslation( ) )
1530 Concrete::Length x0 = textLineMatrix->xt_;
1531 Concrete::Length y0 = textLineMatrix->yt_ + textState_->riseConcrete( );
1532 Concrete::Length xpos = 0;
1534 typedef typeof strings_ ListType;
1535 std::list< double >::const_iterator ki = kernings_.begin( );
1536 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
1538 if( *i != NullPtr< const Lang::String >( ) )
1540 const char * inbuf = (*i)->val_.getPtr( );
1541 char * outbuf = buf;
1542 size_t inbytesleft = strlen( inbuf );
1543 size_t outbytesleft = bufSize;
1544 // The ICONV_CAST macro is defined in config.h.
1545 size_t count = iconv( converter,
1546 ICONV_CAST( & inbuf ), & inbytesleft,
1547 & outbuf, & outbytesleft );
1548 if( count == (size_t)(-1) )
1550 if( errno == EILSEQ )
1552 throw Exceptions::MiscellaneousRequirement( "Not proper UFT-8? Failed to convert to UCS-4 code points." );
1554 else if( errno == EINVAL )
1556 throw Exceptions::MiscellaneousRequirement( "It is suspected that showed text ended with an incomplete multibyte character." );
1558 else if( errno == E2BIG )
1560 throw Exceptions::InternalError( "The buffer allocated for UTF-8 to UCS-4 conversion was too small." );
1562 else
1564 std::ostringstream msg;
1565 msg << "iconv failed with an unrecognized error code: " << errno ;
1566 throw Exceptions::InternalError( strrefdup( msg ) );
1569 for( const char * src = buf; src != outbuf; src += 4 )
1571 Kernel::UnicodeCodePoint code;
1572 code.decode_UCS4( src );
1573 if( code == Kernel::UnicodeCodePoint::SPACE )
1575 // Observe textState_->wordSpacing_
1576 // In addition, it seems reasonable to not let the space character affect the bounding box.
1577 const FontMetrics::CharacterMetrics * charMetrics = horizontalMetrics->charByCode( code, & tmpCharMetric );
1578 xpos += xSize * charMetrics->horizontalCharWidthX_;
1579 xpos += characterTrackKern;
1580 xpos += wordTrackKern;
1582 else if( code == Kernel::UnicodeCodePoint::NEWLINE )
1584 textMatrix->prependShift( Concrete::Coords2D( 0, - textState_->leadingConcrete( ) ) );
1585 textLineMatrix->replaceBy( *textMatrix );
1586 x0 = textLineMatrix->xt_;
1587 y0 = textLineMatrix->yt_ + textState_->riseConcrete( );
1588 xpos = 0;
1590 else
1592 // Observe textState_->characterSpacing_
1593 const FontMetrics::CharacterMetrics * charMetrics = horizontalMetrics->charByCode( code, & tmpCharMetric );
1594 if( Computation::fontMetricGuessIsError && charMetrics == defaultCharMetrics )
1596 std::ostringstream msg;
1597 msg << "Character at offset " << src - buf << " in \"" << buf << "\" was not found in font metrics (and according to your options, guessing is not OK)." ;
1598 throw Exceptions::FontMetricsError( textState_->font_->fontName( ), strrefdup( msg ) );
1600 *xmin = std::min( *xmin, x0 + xpos + xSize * charMetrics->xmin_ );
1601 *ymin = std::min( *ymin, y0 + ySize * charMetrics->ymin_ );
1602 *xmax = std::max( *xmax, x0 + xpos + xSize * charMetrics->xmax_ );
1603 *ymax = std::max( *ymax, y0 + ySize * charMetrics->ymax_ );
1604 xpos += xSize * charMetrics->horizontalCharWidthX_;
1605 xpos += characterTrackKern;
1609 else
1611 if( ki == kernings_.end( ) )
1613 throw Exceptions::InternalError( "Short of kerning values in KernedText::measure." );
1615 xpos -= xSize * *ki;
1616 ++ki;
1619 if( ki != kernings_.end( ) )
1621 throw Exceptions::InternalError( "Too many kerning values in KernedText::writePDFVectorTo." );
1624 textLineMatrix->prependXShift( xpos );
1626 else
1628 Concrete::Length rise = textState_->riseConcrete( );
1630 typedef typeof strings_ ListType;
1631 std::list< double >::const_iterator ki = kernings_.begin( );
1632 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
1634 if( *i != NullPtr< const Lang::String >( ) )
1636 const char * inbuf = (*i)->val_.getPtr( );
1637 char * outbuf = buf;
1638 size_t inbytesleft = strlen( inbuf );
1639 size_t outbytesleft = bufSize;
1640 // The ICONV_CAST macro is defined in config.h.
1641 size_t count = iconv( converter,
1642 ICONV_CAST( & inbuf ), & inbytesleft,
1643 & outbuf, & outbytesleft );
1644 if( count == (size_t)(-1) )
1646 if( errno == EILSEQ )
1648 throw Exceptions::MiscellaneousRequirement( "Not proper UFT-8? Failed to convert to UCS-4 code points." );
1650 else if( errno == EINVAL )
1652 throw Exceptions::MiscellaneousRequirement( "It is suspected that showed text ended with an incomplete multibyte character." );
1654 else if( errno == E2BIG )
1656 throw Exceptions::InternalError( "The buffer allocated for UTF-8 to UCS-4 conversion was too small." );
1658 else
1660 std::ostringstream msg;
1661 msg << "iconv failed with an unrecognized error code: " << errno ;
1662 throw Exceptions::InternalError( strrefdup( msg ) );
1665 for( const char * src = buf; src != outbuf; src += 4 )
1667 Kernel::UnicodeCodePoint code;
1668 code.decode_UCS4( src );
1669 if( code == Kernel::UnicodeCodePoint::SPACE )
1671 // Observe textState_->wordSpacing_
1672 // In addition, it seems reasonable to not let the space character affect the bounding box.
1673 const FontMetrics::CharacterMetrics * charMetrics = horizontalMetrics->charByCode( code, & tmpCharMetric );
1674 textLineMatrix->prependXShift( xSize * charMetrics->horizontalCharWidthX_ + characterTrackKern + wordTrackKern );
1676 else if( code == Kernel::UnicodeCodePoint::NEWLINE )
1678 textMatrix->prependShift( Concrete::Coords2D( 0, - textState_->leadingConcrete( ) ) );
1679 textLineMatrix->replaceBy( *textMatrix );
1681 else
1683 // Observe textState_->characterSpacing_
1684 const FontMetrics::CharacterMetrics * charMetrics = horizontalMetrics->charByCode( code, & tmpCharMetric );
1685 if( Computation::fontMetricGuessIsError && charMetrics == defaultCharMetrics )
1687 std::ostringstream msg;
1688 msg << "Character at offset " << src - buf << " in \"" << buf << "\" was not found in the font metrics (and according to your options, guessing is not OK)." ;
1689 throw Exceptions::FontMetricsError( textState_->font_->fontName( ), strrefdup( msg ) );
1692 Concrete::Coords2D x0y0 = Concrete::Coords2D( xSize * charMetrics->xmin_, ySize * charMetrics->ymin_ + rise ).transformed( *textLineMatrix );
1693 Concrete::Coords2D x0y1 = Concrete::Coords2D( xSize * charMetrics->xmin_, ySize * charMetrics->ymax_ + rise ).transformed( *textLineMatrix );
1694 Concrete::Coords2D x1y0 = Concrete::Coords2D( xSize * charMetrics->xmax_, ySize * charMetrics->ymin_ + rise ).transformed( *textLineMatrix );
1695 Concrete::Coords2D x1y1 = Concrete::Coords2D( xSize * charMetrics->xmax_, ySize * charMetrics->ymax_ + rise ).transformed( *textLineMatrix );
1697 *xmin = std::min( *xmin, x0y0.x_ );
1698 *ymin = std::min( *ymin, x0y0.y_ );
1699 *xmax = std::max( *xmax, x0y0.x_ );
1700 *ymax = std::max( *ymax, x0y0.y_ );
1702 *xmin = std::min( *xmin, x0y1.x_ );
1703 *ymin = std::min( *ymin, x0y1.y_ );
1704 *xmax = std::max( *xmax, x0y1.x_ );
1705 *ymax = std::max( *ymax, x0y1.y_ );
1707 *xmin = std::min( *xmin, x1y0.x_ );
1708 *ymin = std::min( *ymin, x1y0.y_ );
1709 *xmax = std::max( *xmax, x1y0.x_ );
1710 *ymax = std::max( *ymax, x1y0.y_ );
1712 *xmin = std::min( *xmin, x1y1.x_ );
1713 *ymin = std::min( *ymin, x1y1.y_ );
1714 *xmax = std::max( *xmax, x1y1.x_ );
1715 *ymax = std::max( *ymax, x1y1.y_ );
1717 textLineMatrix->prependXShift( xSize * charMetrics->horizontalCharWidthX_ + characterTrackKern );
1721 else
1723 if( ki == kernings_.end( ) )
1725 throw Exceptions::InternalError( "Short of kerning values in KernedText::measure." );
1727 textLineMatrix->prependXShift( - xSize * *ki );
1728 ++ki;
1731 if( ki != kernings_.end( ) )
1733 throw Exceptions::InternalError( "Too many kerning values in KernedText::writePDFVectorTo." );
1738 void
1739 Lang::KernedText::push( Lang::KernedText * dst ) const
1741 typedef typeof strings_ ListType;
1742 std::list< double >::const_iterator ki = kernings_.begin( );
1743 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
1745 if( *i != NullPtr< const Lang::String >( ) )
1747 dst->pushString( *i );
1749 else
1751 dst->pushKerning( *ki );
1752 ++ki;
1758 void
1759 Lang::KernedText::gcMark( Kernel::GCMarkedSet & marked )
1761 typedef typeof strings_ ListType;
1762 for( ListType::const_iterator i = strings_.begin( ); i != strings_.end( ); ++i )
1764 if( *i != NullPtr< const Lang::String >( ) )
1766 const_cast< Lang::String * >( i->getPtr( ) )->gcMark( marked );
1771 Lang::TextNewline::TextNewline( const Concrete::Length tx, const Concrete::Length ty )
1772 : t_( tx, ty )
1775 Lang::TextNewline::~TextNewline( )
1778 void
1779 Lang::TextNewline::show( std::ostream & os ) const
1781 os << "Newline with offset in bp: " << t_ ;
1784 void
1785 Lang::TextNewline::shipout( std::ostream & os, Kernel::PageContentStates * pdfState, const Lang::Transform2D & tf, bool clip ) const
1787 os << t_ << " Td " ;
1790 void
1791 Lang::TextNewline::shipout_outlined( std::ostream & os, Kernel::PageContentStates * pdfState, const Lang::Transform2D & tf, Lang::Transform2D * textMatrix, Lang::Transform2D * lineMatrix, const SimplePDF::PDF_Name * clipContent ) const
1793 lineMatrix->prependShift( t_ );
1794 textMatrix->replaceBy( *lineMatrix );
1798 void
1799 Lang::TextNewline::measure( Lang::Transform2D * textMatrix, Lang::Transform2D * textLineMatrix, Concrete::Length * xmin, Concrete::Length * ymin, Concrete::Length * xmax, Concrete::Length * ymax ) const
1801 textMatrix->prependShift( t_ );
1802 textLineMatrix->replaceBy( *textMatrix );
1805 void
1806 Lang::TextNewline::gcMark( Kernel::GCMarkedSet & marked )
1809 Lang::TextMoveto::TextMoveto( const RefCountPtr< const Lang::Transform2D > & tf )
1810 : tf_( tf )
1813 Lang::TextMoveto::~TextMoveto( )
1816 void
1817 Lang::TextMoveto::show( std::ostream & os ) const
1819 os << "Moveto command" ;
1822 void
1823 Lang::TextMoveto::shipout( std::ostream & os, Kernel::PageContentStates * pdfState, const Lang::Transform2D & tf, bool clip ) const
1825 Lang::Transform2D( tf, *tf_ ).shipout( os );
1826 os << " Tm" << std::endl ;
1829 void
1830 Lang::TextMoveto::shipout_outlined( std::ostream & os, Kernel::PageContentStates * pdfState, const Lang::Transform2D & tf, Lang::Transform2D * textMatrix, Lang::Transform2D * lineMatrix, const SimplePDF::PDF_Name * clipContent ) const
1832 lineMatrix->replaceBy( Lang::Transform2D( tf, *tf_ ) );
1833 textMatrix->replaceBy( *lineMatrix );
1836 void
1837 Lang::TextMoveto::measure( Lang::Transform2D * textMatrix, Lang::Transform2D * textLineMatrix, Concrete::Length * xmin, Concrete::Length * ymin, Concrete::Length * xmax, Concrete::Length * ymax ) const
1839 textMatrix->replaceBy( *tf_ );
1840 textLineMatrix->replaceBy( *textMatrix );
1843 void
1844 Lang::TextMoveto::gcMark( Kernel::GCMarkedSet & marked )
1846 const_cast< Lang::Transform2D * >( tf_.getPtr( ) )->gcMark( marked );
1851 typedef enum { FILL = 0, STROKE, FILLSTROKE, INVISIBLE, FILLCLIP, STROKECLIP, FILLSTROKECLIP, CLIP, UNDEFINED } ValueType;
1852 ValueType mode_;
1854 Lang::TextRenderingMode::TextRenderingMode( const ValueType & mode )
1855 : mode_( mode )
1858 Lang::TextRenderingMode::TextRenderingMode( bool fill, bool stroke, bool clip )
1860 switch( ( fill ? 0x01 : 0x00 ) +
1861 ( stroke ? 0x02 : 0x00 ) +
1862 ( clip ? 0x04 : 0x00 ) )
1864 case 0x00:
1865 mode_ = INVISIBLE;
1866 break;
1867 case 0x01:
1868 mode_ = FILL;
1869 break;
1870 case 0x02:
1871 mode_ = STROKE;
1872 break;
1873 case 0x03:
1874 mode_ = FILLSTROKE;
1875 break;
1876 case 0x04:
1877 mode_ = CLIP;
1878 break;
1879 case 0x05:
1880 mode_ = FILLCLIP;
1881 break;
1882 case 0x06:
1883 mode_ = STROKECLIP;
1884 break;
1885 case 0x07:
1886 mode_ = FILLSTROKECLIP;
1887 break;
1888 default:
1889 throw Exceptions::InternalError( "Semi-static switch out of range in TextRenderingMode::TextRenderingMode." );
1893 Lang::TextRenderingMode::ValueType
1894 Lang::TextRenderingMode::clipMode( ValueType mode )
1896 switch( mode )
1898 case INVISIBLE:
1899 return CLIP;
1900 case FILL:
1901 return FILLCLIP;
1902 case STROKE:
1903 return STROKECLIP;
1904 case FILLSTROKE:
1905 return FILLSTROKECLIP;
1906 default:
1907 return UNDEFINED;
1911 Lang::TextRenderingMode::~TextRenderingMode( )
1914 RefCountPtr< const Lang::Class > Lang::TextRenderingMode::TypeID( new Lang::SystemFinalClass( strrefdup( "TextRenderingMode" ) ) );
1915 TYPEINFOIMPL( TextRenderingMode );
1918 Lang::CharacterSpacingBinding::CharacterSpacingBinding( const Ast::PlacedIdentifier * id, const Ast::DynamicBindingExpression * bindingExpr, const Concrete::Length spacing )
1919 : bindingExpr_( bindingExpr ), spacing_( spacing ), isRelative_( false ), id_( id )
1922 Lang::CharacterSpacingBinding::CharacterSpacingBinding( const Ast::PlacedIdentifier * id, const Ast::DynamicBindingExpression * bindingExpr, const double r )
1923 : bindingExpr_( bindingExpr ), spacing_( r ), isRelative_( true ), id_( id )
1926 Lang::CharacterSpacingBinding::~CharacterSpacingBinding( )
1929 void
1930 Lang::CharacterSpacingBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
1932 if( *sysBindings == 0 )
1934 *sysBindings = new Kernel::SystemDynamicVariables( );
1935 Kernel::TextState * newState = new Kernel::TextState( );
1936 if( isRelative_ )
1938 newState->setCharacterSpacing( Concrete::Length::offtype( spacing_ ) );
1940 else
1942 newState->setCharacterSpacing( spacing_ );
1944 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1945 return;
1948 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
1950 Kernel::TextState * newState = new Kernel::TextState( );
1951 if( isRelative_ )
1953 newState->setCharacterSpacing( Concrete::Length::offtype( spacing_ ) );
1955 else
1957 newState->setCharacterSpacing( spacing_ );
1959 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1960 return;
1963 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
1965 if( newState->hasCharacterSpacing( ) )
1967 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
1970 if( isRelative_ )
1972 newState->setCharacterSpacing( Concrete::Length::offtype( spacing_ ) );
1974 else
1976 newState->setCharacterSpacing( spacing_ );
1978 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
1981 void
1982 Lang::CharacterSpacingBinding::show( std::ostream & os ) const
1984 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":" ;
1985 if( isRelative_ )
1987 os << Concrete::Length::offtype( spacing_ ) ;
1989 else
1991 os << spacing_ / Interaction::displayUnit << Interaction::displayUnitName ;
1995 void
1996 Lang::CharacterSpacingBinding::gcMark( Kernel::GCMarkedSet & marked )
2000 Lang::WordSpacingBinding::WordSpacingBinding( const Ast::PlacedIdentifier * id, const Ast::DynamicBindingExpression * bindingExpr, const Concrete::Length spacing )
2001 : bindingExpr_( bindingExpr ), spacing_( spacing ), isRelative_( false ), id_( id )
2004 Lang::WordSpacingBinding::WordSpacingBinding( const Ast::PlacedIdentifier * id, const Ast::DynamicBindingExpression * bindingExpr, const double r )
2005 : bindingExpr_( bindingExpr ), spacing_( r ), isRelative_( true ), id_( id )
2008 Lang::WordSpacingBinding::~WordSpacingBinding( )
2011 void
2012 Lang::WordSpacingBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
2014 if( *sysBindings == 0 )
2016 *sysBindings = new Kernel::SystemDynamicVariables( );
2017 Kernel::TextState * newState = new Kernel::TextState( );
2018 if( isRelative_ )
2020 newState->setWordSpacing( Concrete::Length::offtype( spacing_ ) );
2022 else
2024 newState->setWordSpacing( spacing_ );
2026 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2027 return;
2030 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
2032 Kernel::TextState * newState = new Kernel::TextState( );
2033 if( isRelative_ )
2035 newState->setWordSpacing( Concrete::Length::offtype( spacing_ ) );
2037 else
2039 newState->setWordSpacing( spacing_ );
2041 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2042 return;
2045 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
2047 if( newState->hasWordSpacing( ) )
2049 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
2052 if( isRelative_ )
2054 newState->setWordSpacing( Concrete::Length::offtype( spacing_ ) );
2056 else
2058 newState->setWordSpacing( spacing_ );
2060 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2063 void
2064 Lang::WordSpacingBinding::show( std::ostream & os ) const
2066 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":" ;
2067 if( isRelative_ )
2069 os << Concrete::Length::offtype( spacing_ ) ;
2071 else
2073 os << spacing_ / Interaction::displayUnit << Interaction::displayUnitName ;
2077 void
2078 Lang::WordSpacingBinding::gcMark( Kernel::GCMarkedSet & marked )
2082 Lang::HorizontalScalingBinding::HorizontalScalingBinding( const Ast::PlacedIdentifier * id, const Ast::DynamicBindingExpression * bindingExpr, double scaling )
2083 : bindingExpr_( bindingExpr ), scaling_( scaling ), id_( id )
2086 Lang::HorizontalScalingBinding::~HorizontalScalingBinding( )
2089 void
2090 Lang::HorizontalScalingBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
2092 if( *sysBindings == 0 )
2094 *sysBindings = new Kernel::SystemDynamicVariables( );
2095 Kernel::TextState * newState = new Kernel::TextState( );
2096 newState->horizontalScaling_ = scaling_;
2097 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2098 return;
2101 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
2103 Kernel::TextState * newState = new Kernel::TextState( );
2104 newState->horizontalScaling_ = scaling_;
2105 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2106 return;
2109 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
2111 if( ! IS_NAN( newState->horizontalScaling_ ) )
2113 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
2116 newState->horizontalScaling_ = scaling_;
2117 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2120 void
2121 Lang::HorizontalScalingBinding::show( std::ostream & os ) const
2123 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":"
2124 << scaling_ ;
2127 void
2128 Lang::HorizontalScalingBinding::gcMark( Kernel::GCMarkedSet & marked )
2132 Lang::LeadingBinding::LeadingBinding( const Ast::PlacedIdentifier * id, const Ast::DynamicBindingExpression * bindingExpr, const Concrete::Length ty )
2133 : bindingExpr_( bindingExpr ), ty_( ty ), isRelative_( false ), id_( id )
2136 Lang::LeadingBinding::LeadingBinding( const Ast::PlacedIdentifier * id, const Ast::DynamicBindingExpression * bindingExpr, const double r )
2137 : bindingExpr_( bindingExpr ), ty_( r ), isRelative_( true ), id_( id )
2140 Lang::LeadingBinding::~LeadingBinding( )
2143 void
2144 Lang::LeadingBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
2146 if( *sysBindings == 0 )
2148 *sysBindings = new Kernel::SystemDynamicVariables( );
2149 Kernel::TextState * newState = new Kernel::TextState( );
2150 if( isRelative_ )
2152 newState->setLeading( Concrete::Length::offtype( ty_ ) );
2154 else
2156 newState->setLeading( ty_ );
2158 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2159 return;
2162 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
2164 Kernel::TextState * newState = new Kernel::TextState( );
2165 if( isRelative_ )
2167 newState->setLeading( Concrete::Length::offtype( ty_ ) );
2169 else
2171 newState->setLeading( ty_ );
2173 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2174 return;
2177 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
2179 if( newState->hasLeading( ) )
2181 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
2184 if( isRelative_ )
2186 newState->setLeading( Concrete::Length::offtype( ty_ ) );
2188 else
2190 newState->setLeading( ty_ );
2192 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2195 void
2196 Lang::LeadingBinding::show( std::ostream & os ) const
2198 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":" ;
2199 if( isRelative_ )
2201 os << Concrete::Length::offtype( ty_ ) ;
2203 else
2205 os << ty_ / Interaction::displayUnit << Interaction::displayUnitName ;
2209 void
2210 Lang::LeadingBinding::gcMark( Kernel::GCMarkedSet & marked )
2214 Lang::FontBinding::FontBinding( const Ast::PlacedIdentifier * id, const Ast::DynamicBindingExpression * bindingExpr, const RefCountPtr< const Lang::Font > & font )
2215 : bindingExpr_( bindingExpr ), font_( font ), id_( id )
2218 Lang::FontBinding::~FontBinding( )
2221 void
2222 Lang::FontBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
2224 if( *sysBindings == 0 )
2226 *sysBindings = new Kernel::SystemDynamicVariables( );
2227 Kernel::TextState * newState = new Kernel::TextState( );
2228 newState->font_ = font_;
2229 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2230 return;
2233 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
2235 Kernel::TextState * newState = new Kernel::TextState( );
2236 newState->font_ = font_;
2237 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2238 return;
2241 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
2243 if( newState->font_ != NullPtr< const Lang::Font >( ) )
2245 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
2248 newState->font_ = font_;
2249 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2252 void
2253 Lang::FontBinding::show( std::ostream & os ) const
2255 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":" ;
2256 font_->show( os );
2259 void
2260 Lang::FontBinding::gcMark( Kernel::GCMarkedSet & marked )
2262 const_cast< Lang::Font * >( font_.getPtr( ) )->gcMark( marked );
2266 Lang::TextSizeBinding::TextSizeBinding( const Ast::PlacedIdentifier * id, const Ast::DynamicBindingExpression * bindingExpr, const Concrete::Length size )
2267 : bindingExpr_( bindingExpr ), size_( size ), id_( id )
2270 Lang::TextSizeBinding::~TextSizeBinding( )
2273 void
2274 Lang::TextSizeBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
2276 if( *sysBindings == 0 )
2278 *sysBindings = new Kernel::SystemDynamicVariables( );
2279 Kernel::TextState * newState = new Kernel::TextState( );
2280 newState->size_ = size_;
2281 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2282 return;
2285 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
2287 Kernel::TextState * newState = new Kernel::TextState( );
2288 newState->size_ = size_;
2289 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2290 return;
2293 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
2295 if( ! IS_NAN( newState->size_ ) )
2297 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
2300 newState->size_ = size_;
2301 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2304 void
2305 Lang::TextSizeBinding::show( std::ostream & os ) const
2307 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":"
2308 << size_ / Interaction::displayUnit << Interaction::displayUnitName ;
2311 void
2312 Lang::TextSizeBinding::gcMark( Kernel::GCMarkedSet & marked )
2316 Lang::TextRenderingModeBinding::TextRenderingModeBinding( const Ast::PlacedIdentifier * id, const Ast::DynamicBindingExpression * bindingExpr, const Lang::TextRenderingMode::ValueType mode )
2317 : bindingExpr_( bindingExpr ), mode_( mode ), id_( id )
2320 Lang::TextRenderingModeBinding::~TextRenderingModeBinding( )
2323 void
2324 Lang::TextRenderingModeBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
2326 if( *sysBindings == 0 )
2328 *sysBindings = new Kernel::SystemDynamicVariables( );
2329 Kernel::TextState * newState = new Kernel::TextState( );
2330 newState->mode_ = mode_;
2331 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2332 return;
2335 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
2337 Kernel::TextState * newState = new Kernel::TextState( );
2338 newState->mode_ = mode_;
2339 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2340 return;
2343 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
2345 if( newState->mode_ != Lang::TextRenderingMode::UNDEFINED )
2347 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
2350 newState->mode_ = mode_;
2351 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2354 void
2355 Lang::TextRenderingModeBinding::show( std::ostream & os ) const
2357 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":"
2358 << "<internal-enum>" ;
2361 void
2362 Lang::TextRenderingModeBinding::gcMark( Kernel::GCMarkedSet & marked )
2366 Lang::TextRiseBinding::TextRiseBinding( const Ast::PlacedIdentifier * id, const Ast::DynamicBindingExpression * bindingExpr, const Concrete::Length ty )
2367 : bindingExpr_( bindingExpr ), ty_( ty ), isRelative_( false ), id_( id )
2370 Lang::TextRiseBinding::TextRiseBinding( const Ast::PlacedIdentifier * id, const Ast::DynamicBindingExpression * bindingExpr, const double r )
2371 : bindingExpr_( bindingExpr ), ty_( r ), isRelative_( true ), id_( id )
2374 Lang::TextRiseBinding::~TextRiseBinding( )
2377 void
2378 Lang::TextRiseBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
2380 if( *sysBindings == 0 )
2382 *sysBindings = new Kernel::SystemDynamicVariables( );
2383 Kernel::TextState * newState = new Kernel::TextState( );
2384 if( isRelative_ )
2386 newState->setRise( Concrete::Length::offtype( ty_ ) );
2388 else
2390 newState->setRise( ty_ );
2392 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2393 return;
2396 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
2398 Kernel::TextState * newState = new Kernel::TextState( );
2399 if( isRelative_ )
2401 newState->setRise( Concrete::Length::offtype( ty_ ) );
2403 else
2405 newState->setRise( ty_ );
2407 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2408 return;
2411 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
2413 if( newState->hasRise( ) )
2415 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
2418 if( isRelative_ )
2420 newState->setRise( Concrete::Length::offtype( ty_ ) );
2422 else
2424 newState->setRise( ty_ );
2426 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2429 void
2430 Lang::TextRiseBinding::show( std::ostream & os ) const
2432 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":" ;
2433 if( isRelative_ )
2435 os << Concrete::Length::offtype( ty_ ) ;
2437 else
2439 os << ty_ / Interaction::displayUnit << Interaction::displayUnitName ;
2443 void
2444 Lang::TextRiseBinding::gcMark( Kernel::GCMarkedSet & marked )
2448 Lang::TextKnockoutBinding::TextKnockoutBinding( const Ast::PlacedIdentifier * id, const Ast::DynamicBindingExpression * bindingExpr, bool knockout )
2449 : bindingExpr_( bindingExpr ), knockout_( knockout ), id_( id )
2452 Lang::TextKnockoutBinding::~TextKnockoutBinding( )
2455 void
2456 Lang::TextKnockoutBinding::bind( MapType & bindings, Kernel::SystemDynamicVariables ** sysBindings ) const
2458 if( *sysBindings == 0 )
2460 *sysBindings = new Kernel::SystemDynamicVariables( );
2461 Kernel::TextState * newState = new Kernel::TextState( );
2462 newState->knockout_ = knockout_;
2463 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2464 return;
2467 if( (*sysBindings)->textState_ == NullPtr< const Kernel::TextState >( ) )
2469 Kernel::TextState * newState = new Kernel::TextState( );
2470 newState->knockout_ = knockout_;
2471 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2472 return;
2475 Kernel::TextState * newState = new Kernel::TextState( *((*sysBindings)->textState_) );
2477 if( ( newState->knockout_ & Kernel::TextState::KNOCKOUT_UNDEFINED_BIT ) != 0 )
2479 throw Exceptions::MultipleDynamicBind( id_, bindingExpr_->idLoc( ), Ast::THE_UNKNOWN_LOCATION );
2482 newState->knockout_ = ( knockout_ ? Kernel::TextState::KNOCKOUT_FLAG_BIT : 0 );
2483 (*sysBindings)->textState_ = RefCountPtr< const Kernel::TextState >( newState );
2486 void
2487 Lang::TextKnockoutBinding::show( std::ostream & os ) const
2489 os << Interaction::DYNAMIC_VARIABLE_PREFIX << id_ << ":"
2490 << ( knockout_ ? "true" : "false" ) ;
2493 void
2494 Lang::TextKnockoutBinding::gcMark( Kernel::GCMarkedSet & marked )
2498 Kernel::CharacterSpacingDynamicVariableProperties::CharacterSpacingDynamicVariableProperties( const Ast::PlacedIdentifier * id )
2499 : Kernel::DynamicVariableProperties( id )
2502 Kernel::CharacterSpacingDynamicVariableProperties::~CharacterSpacingDynamicVariableProperties( )
2505 Kernel::VariableHandle
2506 Kernel::CharacterSpacingDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2508 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2509 return Kernel::VariableHandle( new Kernel::Variable( textState->characterSpacing( ) ) );
2512 void
2513 Kernel::CharacterSpacingDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2515 RefCountPtr< const Lang::Value > valUntyped = val->getUntyped( );
2517 Kernel::ContRef cont = evalState->cont_;
2521 typedef const Lang::Length ArgType;
2522 RefCountPtr< ArgType > spacing = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2523 cont->takeValue( Kernel::ValueRef( new Lang::CharacterSpacingBinding( id_, bindingExpr, spacing->get( ) ) ),
2524 evalState );
2525 return;
2527 catch( const NonLocalExit::NotThisType & ball )
2529 /* Wrong type; never mind!.. but see below!
2535 typedef const Lang::Float ArgType;
2536 RefCountPtr< ArgType > spacing = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2537 cont->takeValue( Kernel::ValueRef( new Lang::CharacterSpacingBinding( id_, bindingExpr, spacing->val_ ) ),
2538 evalState );
2539 return;
2541 catch( const NonLocalExit::NotThisType & ball )
2543 /* Wrong type; never mind!.. but see below!
2547 throw Exceptions::TypeMismatch( bindingExpr->exprLoc( ), valUntyped->getTypeName( ), Helpers::typeSetString( Lang::Length::staticTypeName( ), Lang::Float::staticTypeName( ) ) );
2551 Kernel::WordSpacingDynamicVariableProperties::WordSpacingDynamicVariableProperties( const Ast::PlacedIdentifier * id )
2552 : Kernel::DynamicVariableProperties( id )
2555 Kernel::WordSpacingDynamicVariableProperties::~WordSpacingDynamicVariableProperties( )
2558 Kernel::VariableHandle
2559 Kernel::WordSpacingDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2561 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2562 return Kernel::VariableHandle( new Kernel::Variable( textState->wordSpacing( ) ) );
2565 void
2566 Kernel::WordSpacingDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2568 RefCountPtr< const Lang::Value > valUntyped = val->getUntyped( );
2570 Kernel::ContRef cont = evalState->cont_;
2574 typedef const Lang::Length ArgType;
2575 RefCountPtr< ArgType > spacing = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2576 cont->takeValue( Kernel::ValueRef( new Lang::WordSpacingBinding( id_, bindingExpr, spacing->get( ) ) ),
2577 evalState );
2578 return;
2580 catch( const NonLocalExit::NotThisType & ball )
2582 /* Wrong type; never mind!.. but see below!
2588 typedef const Lang::Float ArgType;
2589 RefCountPtr< ArgType > spacing = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2590 cont->takeValue( Kernel::ValueRef( new Lang::WordSpacingBinding( id_, bindingExpr, spacing->val_ ) ),
2591 evalState );
2592 return;
2594 catch( const NonLocalExit::NotThisType & ball )
2596 /* Wrong type; never mind!.. but see below!
2600 throw Exceptions::TypeMismatch( bindingExpr->exprLoc( ), valUntyped->getTypeName( ), Helpers::typeSetString( Lang::Length::staticTypeName( ), Lang::Float::staticTypeName( ) ) );
2604 Kernel::HorizontalScalingDynamicVariableProperties::HorizontalScalingDynamicVariableProperties( const Ast::PlacedIdentifier * id )
2605 : Kernel::DynamicVariableProperties( id )
2608 Kernel::HorizontalScalingDynamicVariableProperties::~HorizontalScalingDynamicVariableProperties( )
2611 Kernel::VariableHandle
2612 Kernel::HorizontalScalingDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2614 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2615 return Kernel::VariableHandle( Shapes::Helpers::newValHandle( new Lang::Float( textState->horizontalScaling_ ) ) );
2618 void
2619 Kernel::HorizontalScalingDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2621 RefCountPtr< const Lang::Float > scaling = val->getVal< const Lang::Float >( bindingExpr->exprLoc( ) );
2622 Kernel::ContRef cont = evalState->cont_;
2623 cont->takeValue( Kernel::ValueRef( new Lang::HorizontalScalingBinding( id_, bindingExpr, scaling->val_ ) ),
2624 evalState );
2628 Kernel::LeadingDynamicVariableProperties::LeadingDynamicVariableProperties( const Ast::PlacedIdentifier * id )
2629 : Kernel::DynamicVariableProperties( id )
2632 Kernel::LeadingDynamicVariableProperties::~LeadingDynamicVariableProperties( )
2635 Kernel::VariableHandle
2636 Kernel::LeadingDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2638 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2639 return Kernel::VariableHandle( new Kernel::Variable( textState->leading( ) ) );
2642 void
2643 Kernel::LeadingDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2645 RefCountPtr< const Lang::Value > valUntyped = val->getUntyped( );
2647 Kernel::ContRef cont = evalState->cont_;
2651 typedef const Lang::Length ArgType;
2652 RefCountPtr< ArgType > ty = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2653 cont->takeValue( Kernel::ValueRef( new Lang::LeadingBinding( id_, bindingExpr, ty->get( ) ) ),
2654 evalState );
2655 return;
2657 catch( const NonLocalExit::NotThisType & ball )
2659 /* Wrong type; never mind!.. but see below!
2665 typedef const Lang::Float ArgType;
2666 RefCountPtr< ArgType > ty = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2667 cont->takeValue( Kernel::ValueRef( new Lang::LeadingBinding( id_, bindingExpr, ty->val_ ) ),
2668 evalState );
2669 return;
2671 catch( const NonLocalExit::NotThisType & ball )
2673 /* Wrong type; never mind!.. but see below!
2677 throw Exceptions::TypeMismatch( bindingExpr->exprLoc( ), valUntyped->getTypeName( ), Helpers::typeSetString( Lang::Length::staticTypeName( ), Lang::Float::staticTypeName( ) ) );
2681 Kernel::FontDynamicVariableProperties::FontDynamicVariableProperties( const Ast::PlacedIdentifier * id )
2682 : Kernel::DynamicVariableProperties( id )
2685 Kernel::FontDynamicVariableProperties::~FontDynamicVariableProperties( )
2688 Kernel::VariableHandle
2689 Kernel::FontDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2691 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2692 return Kernel::VariableHandle( new Kernel::Variable( textState->font_ ) );
2695 void
2696 Kernel::FontDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2698 Kernel::ContRef cont = evalState->cont_;
2699 cont->takeValue( Kernel::ValueRef( new Lang::FontBinding( id_, bindingExpr, val->getVal< const Lang::Font >( bindingExpr->exprLoc( ) ) ) ),
2700 evalState );
2704 Kernel::TextSizeDynamicVariableProperties::TextSizeDynamicVariableProperties( const Ast::PlacedIdentifier * id )
2705 : Kernel::DynamicVariableProperties( id )
2708 Kernel::TextSizeDynamicVariableProperties::~TextSizeDynamicVariableProperties( )
2711 Kernel::VariableHandle
2712 Kernel::TextSizeDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2714 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2715 return Kernel::VariableHandle( Shapes::Helpers::newValHandle( new Lang::Length( textState->size_ ) ) );
2718 void
2719 Kernel::TextSizeDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2721 RefCountPtr< const Lang::Length > size = val->getVal< const Lang::Length >( bindingExpr->exprLoc( ) );
2722 Kernel::ContRef cont = evalState->cont_;
2723 cont->takeValue( Kernel::ValueRef( new Lang::TextSizeBinding( id_, bindingExpr, size->get( ) ) ),
2724 evalState );
2728 Kernel::TextRenderingModeDynamicVariableProperties::TextRenderingModeDynamicVariableProperties( const Ast::PlacedIdentifier * id )
2729 : Kernel::DynamicVariableProperties( id )
2732 Kernel::TextRenderingModeDynamicVariableProperties::~TextRenderingModeDynamicVariableProperties( )
2735 Kernel::VariableHandle
2736 Kernel::TextRenderingModeDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2738 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2739 return Kernel::VariableHandle( Shapes::Helpers::newValHandle( new Lang::TextRenderingMode( textState->mode_ ) ) );
2742 void
2743 Kernel::TextRenderingModeDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2745 RefCountPtr< const Lang::TextRenderingMode > mode = val->getVal< const Lang::TextRenderingMode >( bindingExpr->exprLoc( ) );
2746 Kernel::ContRef cont = evalState->cont_;
2747 cont->takeValue( Kernel::ValueRef( new Lang::TextRenderingModeBinding( id_, bindingExpr, mode->mode_ ) ),
2748 evalState );
2752 Kernel::TextRiseDynamicVariableProperties::TextRiseDynamicVariableProperties( const Ast::PlacedIdentifier * id )
2753 : Kernel::DynamicVariableProperties( id )
2756 Kernel::TextRiseDynamicVariableProperties::~TextRiseDynamicVariableProperties( )
2759 Kernel::VariableHandle
2760 Kernel::TextRiseDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2762 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2763 return Kernel::VariableHandle( new Kernel::Variable( textState->rise( ) ) );
2766 void
2767 Kernel::TextRiseDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2769 RefCountPtr< const Lang::Value > valUntyped = val->getUntyped( );
2771 Kernel::ContRef cont = evalState->cont_;
2775 typedef const Lang::Length ArgType;
2776 RefCountPtr< ArgType > ty = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2777 cont->takeValue( Kernel::ValueRef( new Lang::TextRiseBinding( id_, bindingExpr, ty->get( ) ) ),
2778 evalState );
2779 return;
2781 catch( const NonLocalExit::NotThisType & ball )
2783 /* Wrong type; never mind!.. but see below!
2789 typedef const Lang::Float ArgType;
2790 RefCountPtr< ArgType > ty = Helpers::try_cast_CoreArgument< ArgType >( valUntyped );
2791 cont->takeValue( Kernel::ValueRef( new Lang::TextRiseBinding( id_, bindingExpr, ty->val_ ) ),
2792 evalState );
2793 return;
2795 catch( const NonLocalExit::NotThisType & ball )
2797 /* Wrong type; never mind!.. but see below!
2801 throw Exceptions::TypeMismatch( bindingExpr->exprLoc( ), valUntyped->getTypeName( ), Helpers::typeSetString( Lang::Length::staticTypeName( ), Lang::Float::staticTypeName( ) ) );
2805 Kernel::TextKnockoutDynamicVariableProperties::TextKnockoutDynamicVariableProperties( const Ast::PlacedIdentifier * id )
2806 : Kernel::DynamicVariableProperties( id )
2809 Kernel::TextKnockoutDynamicVariableProperties::~TextKnockoutDynamicVariableProperties( )
2812 Kernel::VariableHandle
2813 Kernel::TextKnockoutDynamicVariableProperties::fetch( const Kernel::PassedDyn & dyn ) const
2815 RefCountPtr< const Kernel::TextState > textState = dyn->getTextState( );
2816 return Kernel::VariableHandle( Shapes::Helpers::newValHandle( new Lang::Boolean( ( textState->knockout_ & Kernel::TextState::KNOCKOUT_FLAG_BIT ) != 0 ) ) );
2819 void
2820 Kernel::TextKnockoutDynamicVariableProperties::makeBinding( Kernel::VariableHandle val, const Ast::DynamicBindingExpression * bindingExpr, Kernel::EvalState * evalState ) const
2822 RefCountPtr< const Lang::Boolean > mode = val->getVal< const Lang::Boolean >( bindingExpr->exprLoc( ) );
2823 Kernel::ContRef cont = evalState->cont_;
2824 cont->takeValue( Kernel::ValueRef( new Lang::TextKnockoutBinding( id_, bindingExpr, mode->val_ ) ),
2825 evalState );
2830 Kernel::TextState::TextState( )
2831 : relativeFlags_( 0 ),
2832 characterSpacing_( Concrete::Length( std::numeric_limits< double >::signaling_NaN( ) ) ),
2833 wordSpacing_( Concrete::Length( std::numeric_limits< double >::signaling_NaN( ) ) ),
2834 horizontalScaling_( std::numeric_limits< double >::signaling_NaN( ) ),
2835 leading_( Concrete::Length( std::numeric_limits< double >::signaling_NaN( ) ) ),
2836 rise_( Concrete::Length( std::numeric_limits< double >::signaling_NaN( ) ) ),
2837 font_( NullPtr< const Lang::Font >( ) ),
2838 size_( Concrete::Length( std::numeric_limits< double >::signaling_NaN( ) ) ),
2839 mode_( Lang::TextRenderingMode::UNDEFINED ),
2840 knockout_( KNOCKOUT_UNDEFINED_BIT )
2843 Kernel::TextState::TextState( const Kernel::TextState & orig )
2844 : relativeFlags_( orig.relativeFlags_ ),
2845 characterSpacing_( orig.characterSpacing_ ),
2846 wordSpacing_( orig.wordSpacing_ ),
2847 horizontalScaling_( orig.horizontalScaling_ ),
2848 leading_( orig.leading_ ),
2849 rise_( orig.rise_ ),
2850 font_( orig.font_ ),
2851 size_( orig.size_ ),
2852 mode_( orig.mode_ ),
2853 knockout_( orig.knockout_ )
2856 Kernel::TextState::TextState( const Kernel::TextState & newValues, const Kernel::TextState & oldValues )
2857 : relativeFlags_( oldValues.relativeFlags_ ),
2858 characterSpacing_( oldValues.characterSpacing_ ),
2859 wordSpacing_( oldValues.wordSpacing_ ),
2860 horizontalScaling_( oldValues.horizontalScaling_ ),
2861 leading_( oldValues.leading_ ),
2862 rise_( oldValues.rise_ ),
2863 font_( oldValues.font_ ),
2864 size_( oldValues.size_ ),
2865 mode_( oldValues.mode_ ),
2866 knockout_( oldValues.knockout_ )
2868 if( newValues.hasCharacterSpacing( ) )
2870 characterSpacing_ = newValues.characterSpacing_;
2871 relativeFlags_ = ( newValues.characterSpacingIsRelative( ) ? ( relativeFlags_ | RELATIVE_CHARACTER_SPACING ) : ( relativeFlags_ & ~RELATIVE_CHARACTER_SPACING ) );
2873 if( newValues.hasWordSpacing( ) )
2875 wordSpacing_ = newValues.wordSpacing_;
2876 relativeFlags_ = ( newValues.wordSpacingIsRelative( ) ? ( relativeFlags_ | RELATIVE_WORD_SPACING ) : ( relativeFlags_ & ~RELATIVE_WORD_SPACING ) );
2878 if( ! IS_NAN( newValues.horizontalScaling_ ) )
2880 horizontalScaling_ = newValues.horizontalScaling_;
2882 if( newValues.hasLeading( ) )
2884 leading_ = newValues.leading_;
2885 relativeFlags_ = ( newValues.leadingIsRelative( ) ? ( relativeFlags_ | RELATIVE_LEADING ) : ( relativeFlags_ & ~RELATIVE_LEADING ) );
2887 if( newValues.hasRise( ) )
2889 rise_ = newValues.rise_;
2890 relativeFlags_ = ( newValues.riseIsRelative( ) ? ( relativeFlags_ | RELATIVE_RISE ) : ( relativeFlags_ & ~RELATIVE_RISE ) );
2892 if( newValues.font_ != NullPtr< const Lang::Font >( ) )
2894 font_ = newValues.font_;
2896 if( ! IS_NAN( newValues.size_ ) )
2898 size_ = newValues.size_;
2900 if( newValues.mode_ != Lang::TextRenderingMode::UNDEFINED )
2902 mode_ = newValues.mode_;
2904 if( ! ( newValues.knockout_ & KNOCKOUT_UNDEFINED_BIT ) )
2906 knockout_ = newValues.knockout_;
2910 std::map< bool, RefCountPtr< SimplePDF::PDF_Object > > Kernel::TextState::knockoutNameMap_;
2912 Kernel::TextState::TextState( bool setDefaults )
2913 : relativeFlags_( RELATIVE_LEADING ),
2914 characterSpacing_( 0 ),
2915 wordSpacing_( 0 ),
2916 horizontalScaling_( 1 ),
2917 leading_( 1 ),
2918 rise_( 0 ),
2919 font_( Lang::THE_FONT_HELVETICA ),
2920 size_( Concrete::Length( 10 ) ),
2921 mode_( Lang::TextRenderingMode::FILL ),
2922 knockout_( KNOCKOUT_FLAG_BIT ) // this means true
2924 if( ! setDefaults )
2926 throw Exceptions::InternalError( strrefdup( "setDefaults must be true in TextState::TextState." ) );
2930 Kernel::TextState::~TextState( )
2933 void
2934 Kernel::TextState::print( std::ostream & os, const std::string & indentation ) const
2936 if( font_ != NullPtr< const Lang::Font >( ) )
2938 os << indentation ;
2939 Lang::DYNAMIC_VARIABLE_ID_TEXT_FONT.show( os, Ast::Identifier::DYNAMIC_VARIABLE );
2940 os << ": " << font_->fontName( ) << std::endl ;
2942 if( ! IS_NAN( size_ ) )
2944 os << indentation ;
2945 Lang::DYNAMIC_VARIABLE_ID_TEXT_SIZE.show( os, Ast::Identifier::DYNAMIC_VARIABLE );
2946 os << ": " << size_ / Interaction::displayUnit << Interaction::displayUnitName << std::endl ;
2948 os << "(and a bunch of other variables...)" << std::endl ;
2952 void
2953 Kernel::TextState::setLeading( const Concrete::Length leading )
2955 relativeFlags_ &= ~ RELATIVE_LEADING;
2956 leading_ = leading;
2959 void
2960 Kernel::TextState::setLeading( const double relativeLeading )
2962 relativeFlags_ |= RELATIVE_LEADING;
2963 leading_ = Concrete::Length( relativeLeading ); /* We must keep track of this type trick by always looking at relativeFlags_ & RELATIVE_LEADING. */
2966 RefCountPtr< const Lang::Value >
2967 Kernel::TextState::leading( ) const
2969 if( leadingIsRelative( ) )
2971 return RefCountPtr< const Lang::Value >( new Lang::Float( Concrete::Length::offtype( leading_ ) ) );
2973 return RefCountPtr< const Lang::Value >( new Lang::Length( leading_ ) );
2976 Concrete::Length
2977 Kernel::TextState::leadingConcrete( ) const
2979 if( leadingIsRelative( ) )
2981 return Concrete::Length::offtype( leading_ ) * size_;
2983 return leading_;
2986 bool
2987 Kernel::TextState::hasLeading( ) const
2989 return ! IS_NAN( leading_ );
2992 bool
2993 Kernel::TextState::leadingIsRelative( ) const
2995 return ( relativeFlags_ & RELATIVE_LEADING ) != 0;
2999 void
3000 Kernel::TextState::setRise( const Concrete::Length rise )
3002 relativeFlags_ &= ~ RELATIVE_RISE;
3003 rise_ = rise;
3006 void
3007 Kernel::TextState::setRise( const double relativeRise )
3009 relativeFlags_ |= RELATIVE_RISE;
3010 rise_ = Concrete::Length( relativeRise ); // We must keep track of this type trick by always looking at relativeFlags_ & RELATIVE_RISE.
3013 RefCountPtr< const Lang::Value >
3014 Kernel::TextState::rise( ) const
3016 if( riseIsRelative( ) )
3018 return RefCountPtr< const Lang::Value >( new Lang::Float( Concrete::Length::offtype( rise_ ) ) );
3020 return RefCountPtr< const Lang::Value >( new Lang::Length( rise_ ) );
3023 Concrete::Length
3024 Kernel::TextState::riseConcrete( ) const
3026 if( riseIsRelative( ) )
3028 return Concrete::Length::offtype( rise_ ) * size_;
3030 return rise_;
3033 bool
3034 Kernel::TextState::hasRise( ) const
3036 return ! IS_NAN( rise_ );
3039 bool
3040 Kernel::TextState::riseIsRelative( ) const
3042 return ( relativeFlags_ & RELATIVE_RISE ) != 0;
3046 void
3047 Kernel::TextState::setCharacterSpacing( const Concrete::Length spacing )
3049 relativeFlags_ &= ~ RELATIVE_CHARACTER_SPACING;
3050 characterSpacing_ = spacing;
3053 void
3054 Kernel::TextState::setCharacterSpacing( const double relativeSpacing )
3056 relativeFlags_ |= RELATIVE_CHARACTER_SPACING;
3057 characterSpacing_ = Concrete::Length( relativeSpacing ); // We must keep track of this type trick by always looking at the relevant bit in relativeFlags_.
3060 RefCountPtr< const Lang::Value >
3061 Kernel::TextState::characterSpacing( ) const
3063 if( characterSpacingIsRelative( ) )
3065 return RefCountPtr< const Lang::Value >( new Lang::Float( Concrete::Length::offtype( characterSpacing_ ) ) );
3067 return RefCountPtr< const Lang::Value >( new Lang::Length( characterSpacing_ ) );
3070 Concrete::Length
3071 Kernel::TextState::characterSpacingConcrete( ) const
3073 if( characterSpacingIsRelative( ) )
3075 return Concrete::Length::offtype( characterSpacing_ ) * size_;
3077 return characterSpacing_;
3080 bool
3081 Kernel::TextState::hasCharacterSpacing( ) const
3083 return ! IS_NAN( characterSpacing_ );
3086 bool
3087 Kernel::TextState::characterSpacingIsRelative( ) const
3089 return ( relativeFlags_ & RELATIVE_CHARACTER_SPACING ) != 0;
3093 void
3094 Kernel::TextState::setWordSpacing( const Concrete::Length spacing )
3096 relativeFlags_ &= ~ RELATIVE_WORD_SPACING;
3097 wordSpacing_ = spacing;
3100 void
3101 Kernel::TextState::setWordSpacing( const double relativeSpacing )
3103 relativeFlags_ |= RELATIVE_WORD_SPACING;
3104 wordSpacing_ = Concrete::Length( relativeSpacing ); // We must keep track of this type trick by always looking at the relevant bit in relativeFlags_.
3107 RefCountPtr< const Lang::Value >
3108 Kernel::TextState::wordSpacing( ) const
3110 if( wordSpacingIsRelative( ) )
3112 return RefCountPtr< const Lang::Value >( new Lang::Float( Concrete::Length::offtype( wordSpacing_ ) ) );
3114 return RefCountPtr< const Lang::Value >( new Lang::Length( wordSpacing_ ) );
3117 Concrete::Length
3118 Kernel::TextState::wordSpacingConcrete( ) const
3120 if( wordSpacingIsRelative( ) )
3122 return Concrete::Length::offtype( wordSpacing_ ) * size_;
3124 return wordSpacing_;
3127 bool
3128 Kernel::TextState::hasWordSpacing( ) const
3130 return ! IS_NAN( wordSpacing_ );
3133 bool
3134 Kernel::TextState::wordSpacingIsRelative( ) const
3136 return ( relativeFlags_ & RELATIVE_WORD_SPACING ) != 0;
3140 bool
3141 Kernel::TextState::synchAssertKnockout( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool clip, bool force )
3143 assertKnockout( ref );
3144 return synchButKnockout( os, ref, resources, clip, force );
3147 bool
3148 Kernel::TextState::synchCharacterSpacing( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool force )
3150 if( force ||
3151 characterSpacing_ != ref->characterSpacing_ ||
3152 characterSpacingIsRelative( ) != ref->characterSpacingIsRelative( ) )
3154 if( ! ref->hasCharacterSpacing( ) )
3156 return false;
3158 relativeFlags_ = ( ref->characterSpacingIsRelative( ) ? ( relativeFlags_ | RELATIVE_CHARACTER_SPACING ) : ( relativeFlags_ & ~RELATIVE_CHARACTER_SPACING ) );
3159 characterSpacing_ = ref->characterSpacing_;
3160 if( characterSpacingIsRelative( ) )
3162 os << Concrete::Length::offtype( characterSpacing_ ) * Concrete::Length::offtype( ref->size_ ) << " Tc " ;
3164 else
3166 os << Concrete::Length::offtype( characterSpacing_ ) << " Tc " ;
3168 return true;
3170 return false;
3173 bool
3174 Kernel::TextState::synchWordSpacing( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool force )
3176 if( force ||
3177 wordSpacing_ != ref->wordSpacing_ ||
3178 wordSpacingIsRelative( ) != ref->wordSpacingIsRelative( ) )
3180 if( ! ref->hasWordSpacing( ) )
3182 return false;
3184 relativeFlags_ = ( ref->wordSpacingIsRelative( ) ? ( relativeFlags_ | RELATIVE_WORD_SPACING ) : ( relativeFlags_ & ~RELATIVE_WORD_SPACING ) );
3185 wordSpacing_ = ref->wordSpacing_;
3186 if( wordSpacingIsRelative( ) )
3188 os << Concrete::Length::offtype( wordSpacing_ ) * Concrete::Length::offtype( ref->size_ ) << " Tw " ;
3190 else
3192 os << Concrete::Length::offtype( wordSpacing_ ) << " Tw " ;
3194 return true;
3196 return false;
3199 bool
3200 Kernel::TextState::synchHorizontalScaling( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool force )
3202 if( force || horizontalScaling_ != ref->horizontalScaling_ )
3204 if( IS_NAN( ref->horizontalScaling_ ) )
3206 return false;
3208 horizontalScaling_ = ref->horizontalScaling_;
3209 os << 100 * horizontalScaling_ << " Tz " ;
3210 return true;
3212 return false;
3215 bool
3216 Kernel::TextState::synchLeading( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool force )
3218 if( force ||
3219 leading_ != ref->leading_ ||
3220 leadingIsRelative( ) != ref->leadingIsRelative( ) )
3222 if( ! ref->hasLeading( ) )
3224 return false;
3226 relativeFlags_ = ( ref->leadingIsRelative( ) ? ( relativeFlags_ | RELATIVE_LEADING ) : ( relativeFlags_ & ~RELATIVE_LEADING ) );
3227 leading_ = ref->leading_;
3228 if( leadingIsRelative( ) )
3230 os << Concrete::Length::offtype( leading_ ) * Concrete::Length::offtype( ref->size_ ) << " TL " ;
3232 else
3234 os << Concrete::Length::offtype( leading_ ) << " TL " ;
3236 return true;
3238 return false;
3241 bool
3242 Kernel::TextState::synchFontAndSize( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool force )
3244 if( force ||
3245 font_ != ref->font_ ||
3246 size_ != ref->size_ )
3248 if( ref->font_ == NullPtr< const Lang::Font >( ) &&
3249 IS_NAN( ref->size_ ) )
3251 return false;
3253 if( ref->font_ == NullPtr< const Lang::Font >( ) ||
3254 IS_NAN( ref->size_ ) )
3256 throw Exceptions::MiscellaneousRequirement( "It is impossible to leave unspecified only one of font and size." );
3258 bool sizeChanged = ( size_ != ref->size_ );
3259 font_ = ref->font_;
3260 size_ = ref->size_;
3261 os << resources->nameofFont( font_->resource( ) ) << " " << Concrete::Length::offtype( size_ ) << " Tf " ;
3262 if( sizeChanged )
3264 if( ref->hasLeading( ) && ref->leadingIsRelative( ) )
3266 synchLeading( os, ref, resources, true );
3268 if( ref->hasRise( ) && ref->riseIsRelative( ) )
3270 synchRise( os, ref, resources, true );
3272 if( ref->hasCharacterSpacing( ) && ref->characterSpacingIsRelative( ) )
3274 synchCharacterSpacing( os, ref, resources, true );
3276 if( ref->hasWordSpacing( ) && ref->wordSpacingIsRelative( ) )
3278 synchWordSpacing( os, ref, resources, true );
3281 return true;
3283 return false;
3286 bool
3287 Kernel::TextState::synchMode( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool clip, bool force )
3289 Lang::TextRenderingMode::ValueType refMode = clip ? Lang::TextRenderingMode::clipMode( ref->mode_ ) : ref->mode_ ;
3290 if( force || mode_ != refMode )
3292 if( ref->mode_ == Lang::TextRenderingMode::UNDEFINED )
3294 return false;
3296 mode_ = refMode;
3297 os << mode_ << " Tr " ;
3298 return true;
3300 return false;
3303 bool
3304 Kernel::TextState::synchRise( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool force )
3306 if( force ||
3307 rise_ != ref->rise_ ||
3308 riseIsRelative( ) != ref->riseIsRelative( ) )
3310 if( ! ref->hasRise( ) )
3312 return false;
3314 relativeFlags_ = ( ref->riseIsRelative( ) ? ( relativeFlags_ | RELATIVE_RISE ) : ( relativeFlags_ & ~RELATIVE_RISE ) );
3315 rise_ = ref->rise_;
3316 if( riseIsRelative( ) )
3318 os << Concrete::Length::offtype( rise_ ) * Concrete::Length::offtype( ref->size_ ) << " Ts " ;
3320 else
3322 os << Concrete::Length::offtype( rise_ ) << " Ts " ;
3324 return true;
3326 return false;
3329 bool
3330 Kernel::TextState::synchKnockout( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool force )
3332 if( force || knockout_ != ref->knockout_ )
3334 if( ( ref->knockout_ & Kernel::TextState::KNOCKOUT_UNDEFINED_BIT ) != 0 )
3336 return false;
3338 const SimplePDF::PDF_Version::Version KNOCKOUT_VERSION = SimplePDF::PDF_Version::PDF_1_4;
3339 if( ! Kernel::the_PDF_version.greaterOrEqual( KNOCKOUT_VERSION ) )
3341 Kernel::the_PDF_version.message( KNOCKOUT_VERSION, "The text state knockout mode setting was ignored." );
3343 knockout_ = ref->knockout_;
3344 typedef typeof knockoutNameMap_ MapType;
3345 MapType::const_iterator i = knockoutNameMap_.find( knockout_ );
3346 if( i != knockoutNameMap_.end( ) )
3348 os << resources->nameofGraphicsState( i->second ) << " gs " ;
3350 else
3352 RefCountPtr< SimplePDF::PDF_Dictionary > dic;
3353 (*dic)[ "Type" ] = SimplePDF::newName( "ExtGState" );
3354 (*dic)[ "TK" ] = SimplePDF::newBoolean( knockout_ );
3356 RefCountPtr< SimplePDF::PDF_Object > indirection = SimplePDF::indirect( dic, & Kernel::theIndirectObjectCount );
3357 knockoutNameMap_.insert( MapType::value_type( knockout_, indirection ) );
3359 os << resources->nameofGraphicsState( indirection ) << " gs " ;
3361 return true;
3363 return false;
3366 void
3367 Kernel::TextState::assertKnockout( const Kernel::TextState * ref )
3369 if( knockout_ != ref->knockout_ )
3371 throw Exceptions::MiscellaneousRequirement( "PDF does not allow the text knockout mode to change within a text object." );
3375 bool
3376 Kernel::TextState::synchButKnockout( std::ostream & os, const Kernel::TextState * ref, SimplePDF::PDF_Resources * resources, bool clip, bool force )
3378 bool anyChange = false;
3379 anyChange = synchCharacterSpacing( os, ref, resources, force ) || anyChange;
3380 anyChange = synchWordSpacing( os, ref, resources, force ) || anyChange;
3381 anyChange = synchHorizontalScaling( os, ref, resources, force ) || anyChange;
3382 anyChange = synchFontAndSize( os, ref, resources, force ) || anyChange;
3383 anyChange = synchLeading( os, ref, resources, force ) || anyChange; // It is important that this is done after synching the size!
3384 anyChange = synchMode( os, ref, resources, clip, force ) || anyChange;
3385 anyChange = synchRise( os, ref, resources, force ) || anyChange;
3386 if( anyChange )
3388 os << std::endl ;
3390 return anyChange;
3394 Lang::FontMethod_glyph::FontMethod_glyph( RefCountPtr< const Lang::Font > self, const Ast::FileID * fullMethodID )
3395 : Lang::MethodBase< class_type >( self, fullMethodID, false, true )
3397 formals_->appendEvaluatedCoreFormal( "char", Kernel::THE_SLOT_VARIABLE );
3400 Lang::FontMethod_glyph::~FontMethod_glyph( )
3403 void
3404 Lang::FontMethod_glyph::call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const
3406 args.applyDefaults( callLoc );
3408 size_t argsi = 0;
3409 typedef const Lang::Character ArgType;
3411 Kernel::ContRef cont = evalState->cont_;
3412 cont->takeValue( self_->getGlyph( Helpers::down_cast_CoreArgument< ArgType >( coreLoc_, args, argsi, callLoc )->val_,
3413 evalState->dyn_->getTextState( )->size_ ),
3414 evalState );
3415 return;