]> git.lyx.org Git - lyx.git/blob - src/graphics/PreviewLoader.C
Add support for preview.sty 0.73, #ifdef-ed out.
[lyx.git] / src / graphics / PreviewLoader.C
1 /*
2  *  \file PreviewLoader.C
3  *  Copyright 2002 the LyX Team
4  *  Read the file COPYING
5  *
6  * \author Angus Leeming <leeming@lyx.org>
7  */
8
9 #include <config.h>
10
11 #ifdef __GNUG__
12 #pragma implementation
13 #endif
14
15 // Set to 1 if using preview.sty >= 0.73 and a version of lyxpreview2ppm.sh
16 // that extracts the metrics info from the latex log file.
17 #define USING_NEW_PREVIEW_STY 0
18
19 #include "PreviewLoader.h"
20 #include "PreviewImage.h"
21
22 #include "buffer.h"
23 #if !USING_NEW_PREVIEW_STY
24 #include "bufferparams.h"
25 #endif
26 #include "converter.h"
27 #include "debug.h"
28 #include "lyxrc.h"
29 #include "LColor.h"
30
31 #include "insets/inset.h"
32
33 #include "frontends/lyx_gui.h" // hexname
34
35 #include "support/filetools.h"
36 #include "support/forkedcall.h"
37 #include "support/forkedcontr.h"
38 #include "support/lstrings.h"
39 #include "support/lyxlib.h"
40
41 #include <boost/bind.hpp>
42 #include <boost/signals/trackable.hpp>
43
44 #include <fstream>
45 #include <iomanip>
46 #include <list>
47 #include <map>
48 #include <utility>
49 #include <vector>
50
51 using std::endl;
52 using std::find;
53 using std::fill;
54 using std::find_if;
55 using std::getline;
56 using std::make_pair;
57 using std::setfill;
58 using std::setw;
59
60 using std::list;
61 using std::map;
62 using std::ifstream;
63 using std::ofstream;
64 using std::ostream;
65 using std::pair;
66 using std::vector;
67
68 namespace {
69
70 typedef pair<string, string> StrPair;
71
72 // A list of alll snippets to be converted to previews
73 typedef list<string> PendingSnippets;
74
75 // Each item in the vector is a pair<snippet, image file name>.
76 typedef vector<StrPair> BitmapFile;
77
78
79 #if !USING_NEW_PREVIEW_STY
80 double setFontScalingFactor(Buffer &);
81 #endif
82
83 string const unique_filename(string const bufferpath);
84
85 Converter const * setConverter();
86
87 void setAscentFractions(vector<double> & ascent_fractions,
88                         string const & metrics_file);
89
90 struct FindFirst {
91         FindFirst(string const & comp) : comp_(comp) {}
92         bool operator()(StrPair const & sp)
93         {
94                 return sp.first < comp_;
95         }
96 private:
97         string const comp_;
98 };
99
100
101 /// Store info on a currently executing, forked process.
102 struct InProgress {
103         ///
104         InProgress() : pid(0) {}
105         ///
106         InProgress(string const & filename_base,
107                    PendingSnippets const & pending,
108                    string const & to_format);
109         /// Remove any files left lying around and kill the forked process.
110         void stop() const;
111
112         ///
113         pid_t pid;
114         ///
115         string metrics_file;
116         ///
117         BitmapFile snippets;
118 };
119
120 typedef map<string, InProgress>  InProgressProcesses;
121
122 typedef InProgressProcesses::value_type InProgressProcess;
123
124 } // namespace anon
125
126
127 namespace grfx {
128
129 struct PreviewLoader::Impl : public boost::signals::trackable {
130         ///
131         Impl(PreviewLoader & p, Buffer const & b);
132         /// Stop any InProgress items still executing.
133         ~Impl();
134         ///
135         PreviewImage const * preview(string const & latex_snippet) const;
136         ///
137         PreviewLoader::Status status(string const & latex_snippet) const;
138         ///
139         void add(string const & latex_snippet);
140         ///
141         void remove(string const & latex_snippet);
142         ///
143         void startLoading();
144
145         /// Emit this signal when an image is ready for display.
146         boost::signal1<void, PreviewImage const &> imageReady;
147
148         Buffer const & buffer() const { return buffer_; }
149
150 private:
151         /// Called by the Forkedcall process that generated the bitmap files.
152         void finishedGenerating(string const &, pid_t, int);
153         ///
154         void dumpPreamble(ostream &) const;
155         ///
156         void dumpData(ostream &, BitmapFile const &) const;
157
158         /** cache_ allows easy retrieval of already-generated images
159          *  using the LaTeX snippet as the identifier.
160          */
161         typedef boost::shared_ptr<PreviewImage> PreviewImagePtr;
162         ///
163         typedef map<string, PreviewImagePtr> Cache;
164         ///
165         Cache cache_;
166
167         /** pending_ stores the LaTeX snippets in anticipation of them being
168          *  sent to the converter.
169          */
170         PendingSnippets pending_;
171
172         /** in_progress_ stores all forked processes so that we can proceed
173          *  thereafter.
174             The map uses the conversion commands as its identifiers.
175          */
176         InProgressProcesses in_progress_;
177
178         ///
179         PreviewLoader & parent_;
180         ///
181         Buffer const & buffer_;
182         ///
183         double font_scaling_factor_;
184
185         /// We don't own this
186         static Converter const * pconverter_;
187 };
188
189
190 Converter const * PreviewLoader::Impl::pconverter_;
191
192
193 // The public interface, defined in PreviewLoader.h
194 // ================================================
195 PreviewLoader::PreviewLoader(Buffer const & b)
196         : pimpl_(new Impl(*this, b))
197 {}
198
199
200 PreviewLoader::~PreviewLoader()
201 {}
202
203
204 PreviewImage const * PreviewLoader::preview(string const & latex_snippet) const
205 {
206         return pimpl_->preview(latex_snippet);
207 }
208
209
210 PreviewLoader::Status PreviewLoader::status(string const & latex_snippet) const
211 {
212         return pimpl_->status(latex_snippet);
213 }
214
215
216 void PreviewLoader::add(string const & latex_snippet) const
217 {
218         pimpl_->add(latex_snippet);
219 }
220
221
222 void PreviewLoader::remove(string const & latex_snippet) const
223 {
224         pimpl_->remove(latex_snippet);
225 }
226
227
228 void PreviewLoader::startLoading() const
229 {
230         pimpl_->startLoading();
231 }
232
233
234 boost::signals::connection PreviewLoader::connect(slot_type const & slot) const
235 {
236         return pimpl_->imageReady.connect(slot);
237 }
238
239
240 void PreviewLoader::emitSignal(PreviewImage const & pimage) const
241 {
242         pimpl_->imageReady(pimage);
243 }
244
245
246 Buffer const & PreviewLoader::buffer() const
247 {
248         return pimpl_->buffer();
249 }
250
251 } // namespace grfx
252
253
254 // The details of the Impl
255 // =======================
256
257 namespace {
258
259 struct IncrementedFileName {
260         IncrementedFileName(string const & to_format,
261                             string const & filename_base)
262                 : to_format_(to_format), base_(filename_base), counter_(1)
263         {}
264
265         StrPair const operator()(string const & snippet)
266         {
267                 ostringstream os;
268                 os << base_
269                    << setfill('0') << setw(3) << counter_++
270                    << "." << to_format_;
271
272                 string const file = os.str().c_str();
273
274                 return make_pair(snippet, file);
275         }
276
277 private:
278         string const & to_format_;
279         string const & base_;
280         int counter_;
281 };
282
283
284 InProgress::InProgress(string const & filename_base,
285                        PendingSnippets const & pending,
286                        string const & to_format)
287         : pid(0),
288           metrics_file(filename_base + ".metrics"),
289           snippets(pending.size())
290 {
291         PendingSnippets::const_iterator pit  = pending.begin();
292         PendingSnippets::const_iterator pend = pending.end();
293         BitmapFile::iterator sit = snippets.begin();
294
295         std::transform(pit, pend, sit,
296                        IncrementedFileName(to_format, filename_base));
297 }
298
299
300 void InProgress::stop() const
301 {
302         if (pid)
303                 ForkedcallsController::get().kill(pid, 0);
304
305         if (!metrics_file.empty())
306                 lyx::unlink(metrics_file);
307
308         BitmapFile::const_iterator vit  = snippets.begin();
309         BitmapFile::const_iterator vend = snippets.end();
310         for (; vit != vend; ++vit) {
311                 if (!vit->second.empty())
312                         lyx::unlink(vit->second);
313         }
314 }
315
316 } // namespace anon
317
318
319 namespace grfx {
320
321 PreviewLoader::Impl::Impl(PreviewLoader & p, Buffer const & b)
322         : parent_(p), buffer_(b), font_scaling_factor_(0.0)
323 {
324 #if USING_NEW_PREVIEW_STY
325         font_scaling_factor_ = 0.01 * lyxrc.dpi * lyxrc.zoom *
326                 lyxrc.preview_scale_factor;
327 #else
328         font_scaling_factor_ = setFontScalingFactor(const_cast<Buffer &>(b));
329 #endif
330
331         lyxerr[Debug::GRAPHICS] << "The font scaling factor is "
332                                 << font_scaling_factor_ << endl;
333
334         if (!pconverter_)
335                 pconverter_ = setConverter();
336 }
337
338
339 PreviewLoader::Impl::~Impl()
340 {
341         InProgressProcesses::iterator ipit  = in_progress_.begin();
342         InProgressProcesses::iterator ipend = in_progress_.end();
343
344         for (; ipit != ipend; ++ipit) {
345                 ipit->second.stop();
346         }
347 }
348
349
350 PreviewImage const *
351 PreviewLoader::Impl::preview(string const & latex_snippet) const
352 {
353         Cache::const_iterator it = cache_.find(latex_snippet);
354         return (it == cache_.end()) ? 0 : it->second.get();
355 }
356
357
358 namespace {
359
360 struct FindSnippet {
361         FindSnippet(string const & s) : snippet_(s) {}
362         bool operator()(InProgressProcess const & process)
363         {
364                 BitmapFile const & snippets = process.second.snippets;
365                 BitmapFile::const_iterator it  = snippets.begin();
366                 BitmapFile::const_iterator end = snippets.end();
367                 it = find_if(it, end, FindFirst(snippet_));
368                 return it != end;
369         }
370
371 private:
372         string const & snippet_;
373 };
374
375 } // namespace anon
376
377 PreviewLoader::Status
378 PreviewLoader::Impl::status(string const & latex_snippet) const
379 {
380         Cache::const_iterator cit = cache_.find(latex_snippet);
381         if (cit != cache_.end())
382                 return Ready;
383
384         PendingSnippets::const_iterator pit  = pending_.begin();
385         PendingSnippets::const_iterator pend = pending_.end();
386
387         pit = find(pit, pend, latex_snippet);
388         if (pit != pend)
389                 return InQueue;
390
391         InProgressProcesses::const_iterator ipit  = in_progress_.begin();
392         InProgressProcesses::const_iterator ipend = in_progress_.end();
393
394         ipit = find_if(ipit, ipend, FindSnippet(latex_snippet));
395         if (ipit != ipend)
396                 return Processing;
397
398         return NotFound;
399 }
400
401
402 void PreviewLoader::Impl::add(string const & latex_snippet)
403 {
404         if (!pconverter_ || status(latex_snippet) != NotFound)
405                 return;
406
407         string const snippet = trim(latex_snippet);
408         if (snippet.empty())
409                 return;
410
411         lyxerr[Debug::GRAPHICS] << "adding snippet:\n" << snippet << endl;
412
413         pending_.push_back(snippet);
414 }
415
416
417 namespace {
418
419 struct EraseSnippet {
420         EraseSnippet(string const & s) : snippet_(s) {}
421         void operator()(InProgressProcess & process)
422         {
423                 BitmapFile & snippets = process.second.snippets;
424                 BitmapFile::iterator it  = snippets.begin();
425                 BitmapFile::iterator end = snippets.end();
426
427                 it = find_if(it, end, FindFirst(snippet_));
428                 if (it != end)
429                         snippets.erase(it, it+1);
430         }
431
432 private:
433         string const & snippet_;
434 };
435
436 } // namespace anon
437
438
439 void PreviewLoader::Impl::remove(string const & latex_snippet)
440 {
441         Cache::iterator cit = cache_.find(latex_snippet);
442         if (cit != cache_.end())
443                 cache_.erase(cit);
444
445         PendingSnippets::iterator pit  = pending_.begin();
446         PendingSnippets::iterator pend = pending_.end();
447
448         pending_.erase(std::remove(pit, pend, latex_snippet), pend);
449
450         InProgressProcesses::iterator ipit  = in_progress_.begin();
451         InProgressProcesses::iterator ipend = in_progress_.end();
452
453         std::for_each(ipit, ipend, EraseSnippet(latex_snippet));
454
455         for (; ipit != ipend; ++ipit) {
456                 InProgressProcesses::iterator curr = ipit++;
457                 if (curr->second.snippets.empty())
458                         in_progress_.erase(curr);
459         }
460 }
461
462
463 void PreviewLoader::Impl::startLoading()
464 {
465         if (pending_.empty() || !pconverter_)
466                 return;
467
468         lyxerr[Debug::GRAPHICS] << "PreviewLoader::startLoading()" << endl;
469
470         // As used by the LaTeX file and by the resulting image files
471         string const filename_base(unique_filename(buffer_.tmppath));
472
473         // Create an InProgress instance to place in the map of all
474         // such processes if it starts correctly.
475         InProgress inprogress(filename_base, pending_, pconverter_->to);
476
477         // clear pending_, so we're ready to start afresh.
478         pending_.clear();
479
480         // Output the LaTeX file.
481         string const latexfile = filename_base + ".tex";
482
483         ofstream of(latexfile.c_str());
484         of << "\\batchmode\n";
485         dumpPreamble(of);
486         of << "\n\\begin{document}\n";
487         dumpData(of, inprogress.snippets);
488         of << "\n\\end{document}\n";
489         of.close();
490
491         // The conversion command.
492         ostringstream cs;
493         cs << pconverter_->command << " " << latexfile << " "
494            << int(font_scaling_factor_);
495
496         string const command = LibScriptSearch(cs.str().c_str());
497
498         // Initiate the conversion from LaTeX to bitmap images files.
499         Forkedcall::SignalTypePtr convert_ptr;
500         convert_ptr.reset(new Forkedcall::SignalType);
501
502         convert_ptr->connect(
503                 boost::bind(&Impl::finishedGenerating, this, _1, _2, _3));
504
505         Forkedcall call;
506         int ret = call.startscript(command, convert_ptr);
507
508         if (ret != 0) {
509                 lyxerr[Debug::GRAPHICS] << "PreviewLoader::startLoading()\n"
510                                         << "Unable to start process \n"
511                                         << command << endl;
512                 return;
513         }
514
515         // Store the generation process in a list of all such processes
516         inprogress.pid = call.pid();
517         in_progress_[command] = inprogress;
518 }
519
520
521 void PreviewLoader::Impl::finishedGenerating(string const & command,
522                                              pid_t /* pid */, int retval)
523 {
524         string const status = retval > 0 ? "failed" : "succeeded";
525         lyxerr[Debug::GRAPHICS] << "PreviewLoader::finishedInProgress("
526                                 << retval << "): processing " << status
527                                 << " for " << command << endl;
528         if (retval > 0)
529                 return;
530
531         // Paranoia check!
532         InProgressProcesses::iterator git = in_progress_.find(command);
533         if (git == in_progress_.end()) {
534                 lyxerr << "PreviewLoader::finishedGenerating(): unable to find "
535                         "data for\n"
536                        << command << "!" << endl;
537                 return;
538         }
539
540         // Read the metrics file, if it exists
541         vector<double> ascent_fractions(git->second.snippets.size());
542         setAscentFractions(ascent_fractions, git->second.metrics_file);
543
544         // Add these newly generated bitmap files to the cache and
545         // start loading them into LyX.
546         BitmapFile::const_iterator it  = git->second.snippets.begin();
547         BitmapFile::const_iterator end = git->second.snippets.end();
548
549         std::list<PreviewImagePtr> newimages;
550
551         int metrics_counter = 0;
552         for (; it != end; ++it, ++metrics_counter) {
553                 string const & snip = it->first;
554                 string const & file = it->second;
555                 double af = ascent_fractions[metrics_counter];
556
557                 PreviewImagePtr ptr(new PreviewImage(parent_, snip, file, af));
558                 cache_[snip] = ptr;
559
560                 newimages.push_back(ptr);
561         }
562
563         // Remove the item from the list of still-executing processes.
564         in_progress_.erase(git);
565
566         // Tell the outside world
567         std::list<PreviewImagePtr>::const_iterator nit  = newimages.begin();
568         std::list<PreviewImagePtr>::const_iterator nend = newimages.end();
569         for (; nit != nend; ++nit) {
570                 imageReady(*nit->get());
571         }
572 }
573
574
575 void PreviewLoader::Impl::dumpPreamble(ostream & os) const
576 {
577         // Why on earth is Buffer::makeLaTeXFile a non-const method?
578         Buffer & tmp = const_cast<Buffer &>(buffer_);
579         // Dump the preamble only.
580         tmp.makeLaTeXFile(os, string(), true, false, true);
581
582         // Loop over the insets in the buffer and dump all the math-macros.
583         Buffer::inset_iterator it  = buffer_.inset_const_iterator_begin();
584         Buffer::inset_iterator end = buffer_.inset_const_iterator_end();
585
586         for (; it != end; ++it) {
587                 if ((*it)->lyxCode() == Inset::MATHMACRO_CODE) {
588                         (*it)->latex(&buffer_, os, true, true);
589                 }
590         }
591
592         // All equation lables appear as "(#)" + preview.sty's rendering of
593         // the label name
594         if (lyxrc.preview_hashed_labels)
595                 os << "\\renewcommand{\\theequation}{\\#}\n";
596
597         // Use the preview style file to ensure that each snippet appears on a
598         // fresh page.
599         os << "\n"
600            << "\\usepackage[active,delayed,dvips,tightpage,showlabels,lyx]{preview}\n"
601            << "\n";
602
603         // This piece of PostScript magic ensures that the foreground and
604         // background colors are the same as the LyX screen.
605         string fg = lyx_gui::hexname(LColor::preview);
606         if (fg.empty()) fg = "000000";
607
608         string bg = lyx_gui::hexname(LColor::background);
609         if (bg.empty()) bg = "ffffff";
610
611         os << "\\AtBeginDocument{\\AtBeginDvi{%\n"
612            << "\\special{!userdict begin/bop-hook{//bop-hook exec\n"
613            << "<" << fg << bg << ">{255 div}forall setrgbcolor\n"
614            << "clippath fill setrgbcolor}bind def end}}}\n";
615 }
616
617
618 void PreviewLoader::Impl::dumpData(ostream & os,
619                                    BitmapFile const & vec) const
620 {
621         if (vec.empty())
622                 return;
623
624         BitmapFile::const_iterator it  = vec.begin();
625         BitmapFile::const_iterator end = vec.end();
626
627         for (; it != end; ++it) {
628                 os << "\\begin{preview}\n"
629                    << it->first
630                    << "\n\\end{preview}\n\n";
631         }
632 }
633
634 } // namespace grfx
635
636
637 namespace {
638
639 string const unique_filename(string const bufferpath)
640 {
641         static int theCounter = 0;
642         string const filename = tostr(theCounter++) + "lyxpreview";
643         return AddName(bufferpath, filename);
644 }
645
646
647 Converter const * setConverter()
648 {
649         string const from = "lyxpreview";
650
651         Formats::FormatList::const_iterator it  = formats.begin();
652         Formats::FormatList::const_iterator end = formats.end();
653
654         for (; it != end; ++it) {
655                 string const to = it->name();
656                 if (from == to)
657                         continue;
658
659                 Converter const * ptr = converters.getConverter(from, to);
660                 if (ptr)
661                         return ptr;
662         }
663
664         static bool first = true;
665         if (first) {
666                 first = false;
667                 lyxerr << "PreviewLoader::startLoading()\n"
668                        << "No converter from \"lyxpreview\" format has been "
669                         "defined."
670                        << endl;
671         }
672
673         return 0;
674 }
675
676
677 #if !USING_NEW_PREVIEW_STY
678 double setFontScalingFactor(Buffer & buffer)
679 {
680         double scale_factor = 0.01 * lyxrc.dpi * lyxrc.zoom *
681                 lyxrc.preview_scale_factor;
682
683         // Has the font size been set explicitly?
684         string const & fontsize = buffer.params.fontsize;
685         lyxerr[Debug::GRAPHICS] << "PreviewLoader::scaleToFitLyXView()\n"
686                                 << "font size is " << fontsize << endl;
687
688         if (isStrUnsignedInt(fontsize))
689                 return 10.0 * scale_factor / strToDbl(fontsize);
690
691         // No. We must extract it from the LaTeX class file.
692         LyXTextClass const & tclass = buffer.params.getLyXTextClass();
693         string const textclass(tclass.latexname() + ".cls");
694         string const classfile(findtexfile(textclass, "cls"));
695
696         lyxerr[Debug::GRAPHICS] << "text class is " << textclass << '\n'
697                                 << "class file is " << classfile << endl;
698
699         ifstream ifs(classfile.c_str());
700         if (!ifs.good()) {
701                 lyxerr[Debug::GRAPHICS] << "Unable to open class file!" << endl;
702                 return scale_factor;
703         }
704
705         string str;
706         double scaling = scale_factor;
707
708         while (ifs.good()) {
709                 getline(ifs, str);
710                 // To get the default font size, look for a line like
711                 // "\ExecuteOptions{letterpaper,10pt,oneside,onecolumn,final}"
712                 if (!prefixIs(ltrim(str), "\\ExecuteOptions"))
713                         continue;
714
715                 // str contains just the options of \ExecuteOptions
716                 string const tmp = split(str, '{');
717                 split(tmp, str, '}');
718
719                 int count = 0;
720                 string tok = token(str, ',', count++);
721                 while (!isValidLength(tok) && !tok.empty())
722                         tok = token(str, ',', count++);
723
724                 if (!tok.empty()) {
725                         lyxerr[Debug::GRAPHICS]
726                                 << "Extracted default font size from "
727                                 "LaTeX class file successfully!" << endl;
728                         LyXLength fsize(tok);
729                         scaling *= 10.0 / fsize.value();
730                         break;
731                 }
732         }
733
734         return scaling;
735 }
736 #endif
737
738
739 void setAscentFractions(vector<double> & ascent_fractions,
740                         string const & metrics_file)
741 {
742         // If all else fails, then the images will have equal ascents and
743         // descents.
744         vector<double>::iterator it  = ascent_fractions.begin();
745         vector<double>::iterator end = ascent_fractions.end();
746         fill(it, end, 0.5);
747
748         ifstream in(metrics_file.c_str());
749         if (!in.good()) {
750                 lyxerr[Debug::GRAPHICS]
751                         << "setAscentFractions(" << metrics_file << ")\n"
752                         << "Unable to open file!" << endl;
753                 return;
754         }
755
756         bool error = false;
757
758 #if USING_NEW_PREVIEW_STY
759         // Tightpage dimensions affect all subsequent dimensions
760         int tp_ascent;
761         int tp_descent;
762
763         int snippet_counter = 0;
764         while (!in.eof()) {
765                 // Expecting lines of the form
766                 // Preview: Tightpage tp_bl_x tp_bl_y tp_tr_x tp_tr_y
767                 // Preview: Snippet id ascent descent width
768                 string preview;
769                 string type;
770                 in >> preview >> type;
771
772                 if (!in.good())
773                         // eof after all
774                         break;
775
776                 error = preview != "Preview:"
777                         || (type != "Tightpage" && type != "Snippet");
778                 if (error)
779                         break;
780
781                 if (type == "Tightpage") {
782                         int dummy;
783                         in >> dummy >> tp_descent >> dummy >> tp_ascent;
784
785                         error = !in.good();
786                         if (error)
787                                 break;
788
789                 } else {
790                         int dummy;
791                         int snippet_id;
792                         int ascent;
793                         int descent;
794                         in >> snippet_id >> ascent >> descent >> dummy;
795
796                         error = !in.good() || ++snippet_counter != snippet_id;
797                         if (error)
798                                 break;
799
800                         double const a = ascent + tp_ascent;
801                         double const d = descent - tp_descent;
802
803                         if (!lyx::float_equal(a + d, 0, 0.1))
804                                 *it = a / (a + d);
805
806                         if (++it == end)
807                                 break;
808                 }
809         }
810
811 #else
812         int snippet_counter = 0;
813         for (; it != end; ++it) {
814                 // Extracting lines of the form
815                 // %%Page id: tp_bl_x tp_bl_y tp_tr_x tp_tr_y asc desc width
816                 string page;
817                 string page_id;
818                 int dummy;
819                 int tp_ascent;
820                 int tp_descent;
821                 int ascent;
822                 int descent;
823                 in >> page >> page_id
824                    >> dummy >> tp_descent >> dummy >> tp_ascent
825                    >> ascent >> descent >> dummy;
826
827                 page_id = rtrim(page_id, ":");
828
829                 error = !in.good()
830                         || !isStrUnsignedInt(page_id);
831                 if (error)
832                         break;
833
834                 int const snippet_id = strToInt(page_id);
835                 error = page != "%%Page"
836                         || ++snippet_counter != snippet_id;
837                 if (error)
838                         break;
839
840                 double const a = ascent + tp_ascent;
841                 double const d = descent - tp_descent;
842
843                 if (!lyx::float_equal(a + d, 0, 0.1))
844                         *it = a / (a + d);
845         }
846 #endif
847
848         if (error) {
849                 lyxerr[Debug::GRAPHICS]
850                         << "setAscentFractions(" << metrics_file << ")\n"
851                         << "Error reading file!\n" << endl;
852         }
853 }
854
855 } // namespace anon