From e8048d66868e0eae8ef4415d9397896d9c18221e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 7 May 2023 19:51:15 +0200 Subject: [PATCH] WIP: preparations - introducing TextInfo --- src/db/db/dbHershey.cc | 152 +++++++++---------- src/db/db/dbHershey.h | 17 ++- src/laybasic/laybasic/layBitmap.cc | 59 ++------ src/laybasic/laybasic/layFinder.cc | 2 +- src/laybasic/laybasic/layLayoutCanvas.h | 7 +- src/laybasic/laybasic/layLayoutViewBase.h | 8 + src/laybasic/laybasic/layTextInfo.cc | 170 ++++++++++++++++++++++ src/laybasic/laybasic/layTextInfo.h | 89 +++++++++++ src/laybasic/laybasic/laybasic.pro | 2 + src/tl/tl/tlString.cc | 16 ++ src/tl/tl/tlString.h | 31 ++++ 11 files changed, 413 insertions(+), 140 deletions(-) create mode 100644 src/laybasic/laybasic/layTextInfo.cc create mode 100644 src/laybasic/laybasic/layTextInfo.h diff --git a/src/db/db/dbHershey.cc b/src/db/db/dbHershey.cc index 62b311d37..41e0726d3 100644 --- a/src/db/db/dbHershey.cc +++ b/src/db/db/dbHershey.cc @@ -101,17 +101,22 @@ hershey_count_edges (const std::string &s, unsigned int f) HersheyFont *fp = fonts [f]; size_t n = 0; - for (const char *cp = s.c_str (); *cp; ++cp) { + for (const char *cp = s.c_str (); *cp; ) { - unsigned char c = (unsigned char) *cp; - if (c == '\012' || c == '\015') { - if (c == '\015' && cp[1] == '\012') { - ++cp; + if (tl::skip_newline (cp)) { + + // skip new line - they don't contribute edges + + } else { + + uint32_t c = tl::utf32_from_utf8 (cp); + + if (c < fp->end_char && c >= fp->start_char) { + n += fp->chars [c - fp->start_char].edge_end - fp->chars [c - fp->start_char].edge_start; + } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { + n += fp->chars [invalid_char - fp->start_char].edge_end - fp->chars [invalid_char - fp->start_char].edge_start; } - } else if (c < fp->end_char && c >= fp->start_char) { - n += fp->chars [c - fp->start_char].edge_end - fp->chars [c - fp->start_char].edge_start; - } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { - n += fp->chars [invalid_char - fp->start_char].edge_end - fp->chars [invalid_char - fp->start_char].edge_start; + } } @@ -142,22 +147,27 @@ hershey_text_box (const std::string &s, unsigned int f) int w = 0; int h = fp->ymax; - for (const char *cp = s.c_str (); *cp; ++cp) { + for (const char *cp = s.c_str (); *cp; ) { + + if (tl::skip_newline (cp)) { - unsigned char c = (unsigned char) *cp; - if (c == '\012' || c == '\015') { - if (c == '\015' && cp[1] == '\012') { - ++cp; - } if (w > wl) { wl = w; } + hl += line_spacing + h - fp->ymin; w = 0; - } else if (c < fp->end_char && c >= fp->start_char) { - w += fp->chars [c - fp->start_char].width; - } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { - w += fp->chars [invalid_char - fp->start_char].width; + + } else { + + uint32_t c = tl::utf32_from_utf8 (cp); + + if (c < fp->end_char && c >= fp->start_char) { + w += fp->chars [c - fp->start_char].width; + } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { + w += fp->chars [invalid_char - fp->start_char].width; + } + } } @@ -170,7 +180,7 @@ hershey_text_box (const std::string &s, unsigned int f) return db::DBox (0, 0, wl, hl); } -void +void hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halign, VAlign valign, std::vector &linestarts) { HersheyFont *fp = fonts [f]; @@ -179,20 +189,24 @@ hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halig int w = 0; int h = fp->ymax; - for (const char *cp = s.c_str (); *cp; ++cp) { + for (const char *cp = s.c_str (); *cp; ) { + + if (tl::skip_newline (cp)) { - unsigned char c = (unsigned char) *cp; - if (c == '\012' || c == '\015') { - if (c == '\015' && cp[1] == '\012') { - ++cp; - } linestarts.push_back (db::DPoint (w, -hl)); hl += line_spacing + h - fp->ymin; w = 0; - } else if (c < fp->end_char && c >= fp->start_char) { - w += fp->chars [c - fp->start_char].width; - } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { - w += fp->chars [invalid_char - fp->start_char].width; + + } else { + + uint32_t c = tl::utf32_from_utf8 (cp); + + if (c < fp->end_char && c >= fp->start_char) { + w += fp->chars [c - fp->start_char].width; + } else if (invalid_char < fp->end_char && invalid_char >= fp->start_char) { + w += fp->chars [invalid_char - fp->start_char].width; + } + } } @@ -221,20 +235,18 @@ hershey_justify (const std::string &s, unsigned int f, db::DBox bx, HAlign halig } *l = p; } - } // ---------------------------------------------------------------------------- // basic_hershey_edge_iterator implementation basic_hershey_edge_iterator::basic_hershey_edge_iterator (const std::string &s, unsigned int f, const std::vector &line_starts) - : m_line (0), m_string (s), m_edge (0), m_edge_end (0), m_index (0), m_linestarts (line_starts) + : m_line (0), m_string (s), m_edge (0), m_edge_end (0), m_linestarts (line_starts) { m_fp = fonts [f]; - m_end = (unsigned int) m_string.size (); - m_new_char = true; + mp_cp = m_string.c_str (); - if (m_linestarts.size () == 0) { + if (m_linestarts.empty ()) { m_linestarts.push_back (db::DPoint (0.0, 0.0)); } m_pos = m_linestarts [0]; @@ -243,66 +255,47 @@ basic_hershey_edge_iterator::basic_hershey_edge_iterator (const std::string &s, bool basic_hershey_edge_iterator::at_end () const { - return m_index >= m_end; + return *mp_cp == 0 && m_edge == m_edge_end; } db::DEdge basic_hershey_edge_iterator::get () { - while (m_new_char && !at_end ()) { - - unsigned char c = (unsigned char) (m_index < m_end ? m_string [m_index] : ' '); + while (m_edge == m_edge_end && *mp_cp) { m_pos += m_delta; - if (c < m_fp->end_char && c >= m_fp->start_char) { + m_edge = m_edge_end = 0; + m_delta = db::DVector (); - m_edge = m_fp->chars [c - m_fp->start_char].edge_start; - m_edge_end = m_fp->chars [c - m_fp->start_char].edge_end; - m_delta = db::DVector (m_fp->chars [c - m_fp->start_char].width, 0); + if (tl::skip_newline (mp_cp)) { - } else if (c != '\012' && c != '\015' - && invalid_char < m_fp->end_char - && invalid_char >= m_fp->start_char) { + ++m_line; - m_edge = m_fp->chars [invalid_char - m_fp->start_char].edge_start; - m_edge_end = m_fp->chars [invalid_char - m_fp->start_char].edge_end; - m_delta = db::DVector (m_fp->chars [invalid_char - m_fp->start_char].width, 0); + if (m_line >= m_linestarts.size ()) { + db::DPoint last; + last = m_linestarts.back (); + last += db::DVector (0, -(m_fp->ymax - m_fp->ymin + line_spacing)); + m_linestarts.push_back (last); + } + m_pos = m_linestarts [m_line]; } else { - m_edge = m_edge_end = 0; - m_delta = db::DVector (); + uint32_t c = tl::utf32_from_utf8 (mp_cp); - } - - if (m_edge == m_edge_end) { - - if (c == '\012' || c == '\015') { - if (c == '\015' && m_string.size () > m_index + 1 && m_string [m_index] == '\012') { - ++m_index; - } - ++m_line; - if (m_line >= m_linestarts.size ()) { - m_linestarts.push_back (m_linestarts.back () + db::DVector (0.0, -(m_fp->ymax - m_fp->ymin + line_spacing))); - } - m_pos = m_linestarts [m_line]; + if (c < m_fp->start_char || c >= m_fp->end_char) { + c = invalid_char; } - ++m_index; + if (c < m_fp->end_char && c >= m_fp->start_char) { + m_edge = m_fp->chars [c - m_fp->start_char].edge_start; + m_edge_end = m_fp->chars [c - m_fp->start_char].edge_end; + m_delta = db::DVector (m_fp->chars [c - m_fp->start_char].width, 0); + } - } else { - m_new_char = false; } - } - while (m_line >= m_linestarts.size ()) { - db::DPoint last; - if (m_linestarts.size () > 0) { - last = m_linestarts.back (); - last += db::DVector (0, -(m_fp->ymax - m_fp->ymin + line_spacing)); - } - m_linestarts.push_back (last); } if (!at_end ()) { @@ -316,16 +309,9 @@ basic_hershey_edge_iterator::get () void basic_hershey_edge_iterator::inc () { - if (m_new_char) { - get (); - } - if (! at_end ()) { ++m_edge; - if (m_edge == m_edge_end) { - ++m_index; - m_new_char = true; - } + get (); } } diff --git a/src/db/db/dbHershey.h b/src/db/db/dbHershey.h index 1e6b4990e..85387e4fc 100644 --- a/src/db/db/dbHershey.h +++ b/src/db/db/dbHershey.h @@ -51,11 +51,10 @@ public: void inc (); private: - bool m_new_char; unsigned int m_line; + const char *mp_cp; std::string m_string; unsigned int m_edge, m_edge_end; - unsigned int m_index, m_end; std::vector m_linestarts; db::DPoint m_pos; db::DVector m_delta; @@ -177,14 +176,16 @@ struct DB_PUBLIC_TEMPLATE hershey /** * @brief Obtain the size of the text * - * @return The bounding box of the text with the scaling applied + * @return The bounding box of the text with the scaling and justification applied */ - box bbox () const + db::DBox bbox () const { - db::DBox b = hershey_text_box (m_string, m_font); - db::point p1 (coord_traits::rounded (b.p1 ().x () / m_scale), coord_traits::rounded (b.p1 ().y () / m_scale)); - db::point p2 (coord_traits::rounded (b.p2 ().x () / m_scale), coord_traits::rounded (b.p2 ().y () / m_scale)); - return box (p1, p2); + db::DBox b = hershey_text_box (m_string, m_font) * (1.0 / m_scale); + if (! m_linestarts.empty ()) { + return b.moved (m_linestarts.front () - db::DPoint ()); + } else { + return b; + } } /** diff --git a/src/laybasic/laybasic/layBitmap.cc b/src/laybasic/laybasic/layBitmap.cc index af0a981f1..8755cab91 100644 --- a/src/laybasic/laybasic/layBitmap.cc +++ b/src/laybasic/laybasic/layBitmap.cc @@ -791,35 +791,6 @@ Bitmap::render_contour (std::vector &edges) } } -static unsigned char next_char_latin1_from_utf8 (const char *&cp, const char *cpf = 0) -{ - unsigned char c = *cp; - if ((c & 0xe0) == 0xc0) { - if ((cp[1] & 0xc0) == 0x80 && (! cpf || cpf > cp + 1)) { - unsigned int x = ((unsigned int) ((unsigned char) c & 0x1f) << 6) | (unsigned int) (cp[1] & 0x3f); - cp += 1; - if (x < 255) { - c = x; - } else { - c = '?'; - } - } else { - c = '?'; - } - } else if ((c & 0xf0) == 0xe0) { - if ((cp[1] & 0xc0) == 0x80 && (cp[2] & 0xc0) == 0x80 && (! cpf || cpf > cp + 2)) { - cp += 2; - } - c = '?'; - } else if ((c & 0xf8) == 0xf0) { - if ((cp[1] & 0xc0) == 0x80 && (cp[2] & 0xc0) == 0x80 && (cp[3] & 0xc0) == 0x80 && (! cpf || cpf > cp + 3)) { - cp += 3; - } - c = '?'; - } - return c; -} - void Bitmap::render_text (const lay::RenderText &text) { @@ -830,12 +801,11 @@ Bitmap::render_text (const lay::RenderText &text) // count the lines and max. characters per line unsigned int lines = 1; - for (const char *cp = text.text.c_str (); *cp; ++cp) { - if (*cp == '\012' || *cp == '\015') { - if (*cp == '\015' && cp[1] == '\012') { - ++cp; - } + for (const char *cp = text.text.c_str (); *cp; ) { + if (tl::skip_newline (cp)) { ++lines; + } else { + ++cp; } } @@ -858,10 +828,9 @@ Bitmap::render_text (const lay::RenderText &text) unsigned int length = 0; const char *cp = cp1; - while (*cp && *cp != '\012' && *cp != '\015') { - next_char_latin1_from_utf8 (cp); + while (*cp && !tl::is_newline (*cp)) { + tl::utf32_from_utf8 (cp); ++length; - ++cp; } double xx; @@ -880,11 +849,13 @@ Bitmap::render_text (const lay::RenderText &text) for ( ; cp1 != cp; ++cp1) { - unsigned char c = next_char_latin1_from_utf8 (cp1, cp); + uint32_t c = tl::utf32_from_utf8 (cp1, cp); + if (c < uint32_t (ff.first_char ()) || c >= uint32_t (ff.n_chars ()) + ff.first_char ()) { + // NOTE: '?' needs to be a valid character always + c = uint32_t ('?'); + } - size_t cc = c; // to suppress a compiler warning .. - if (c >= ff.first_char () && cc < size_t (ff.n_chars ()) + size_t (ff.first_char ()) - && xx > -100.0 && xx < double (width ())) { + if (xx > -100.0 && xx < double (width ())) { fill_pattern (int (y + 0.5), int (floor (xx)), ff.data () + (c - ff.first_char ()) * ff.height () * ff.stride (), ff.stride (), ff.height ()); } @@ -897,11 +868,7 @@ Bitmap::render_text (const lay::RenderText &text) } // next line - if (*cp1 == '\012' || *cp1 == '\015') { - if (*cp1 == '\015' && cp1[1] == '\012') { - ++cp1; - } - ++cp1; + if (tl::skip_newline (cp1)) { y -= double (ff.line_height ()); } diff --git a/src/laybasic/laybasic/layFinder.cc b/src/laybasic/laybasic/layFinder.cc index 6fa5b6661..84903fad6 100644 --- a/src/laybasic/laybasic/layFinder.cc +++ b/src/laybasic/laybasic/layFinder.cc @@ -894,5 +894,5 @@ InstFinder::visit_cell (const db::Cell &cell, const db::Box &search_box, const d } -} // namespace edt +} // namespace lay diff --git a/src/laybasic/laybasic/layLayoutCanvas.h b/src/laybasic/laybasic/layLayoutCanvas.h index f92a48855..938219763 100644 --- a/src/laybasic/laybasic/layLayoutCanvas.h +++ b/src/laybasic/laybasic/layLayoutCanvas.h @@ -320,6 +320,11 @@ public: return m_line_styles; } + /** + * @brief Reimplementation of ViewObjectCanvas: Resolution + */ + double resolution () const; + /** * @brief Reimplementation of ViewObjectCanvas: Background color */ @@ -444,8 +449,6 @@ private: void do_redraw_all (bool force_redraw = true); void prepare_drawing (); - virtual double resolution () const; - const std::vector &scaled_view_ops (unsigned int lw); }; diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index d3328384b..351830fc7 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -1721,6 +1721,14 @@ public: return mp_canvas; } + /** + * @brief Gets the canvas object (const version) + */ + const lay::LayoutCanvas *canvas () const + { + return mp_canvas; + } + #if defined(HAVE_QT) /** * @brief Gets the layer control panel diff --git a/src/laybasic/laybasic/layTextInfo.cc b/src/laybasic/laybasic/layTextInfo.cc new file mode 100644 index 000000000..28c737580 --- /dev/null +++ b/src/laybasic/laybasic/layTextInfo.cc @@ -0,0 +1,170 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "layTextInfo.h" +#include "layFixedFont.h" +#include "layLayoutViewBase.h" +#include "layLayoutCanvas.h" + +namespace lay +{ + +TextInfo::TextInfo (const LayoutViewBase *view, const db::DCplxTrans &vp_trans) + : m_default_text_size (view->default_text_size ()), + m_default_font (db::Font (view->text_font ())), + m_apply_text_trans (view->apply_text_trans ()), + m_resolution (view->canvas ()->resolution ()), + m_vp_trans (vp_trans) +{ + // .. nothing yet .. +} + +TextInfo::TextInfo (double default_text_size, const db::Font &default_font, bool apply_text_trans, double resolution, const db::DCplxTrans &vp_trans) + : m_default_text_size (default_text_size), + m_default_font (default_font), + m_apply_text_trans (apply_text_trans), + m_resolution (resolution), + m_vp_trans (vp_trans) +{ + // .. nothing yet .. +} + +db::DBox +TextInfo::get_bbox (const db::DText &text) const +{ + // offset in pixels (space between origin and text) + const double offset = 2.0; + + db::DFTrans fp (db::DFTrans::r0); + db::DCoord h; + db::Font font = text.font () == db::NoFont ? m_default_font : text.font (); + + if (m_apply_text_trans && font != db::NoFont && font != db::DefaultFont) { + fp = db::DFTrans (m_vp_trans.fp_trans () * text.trans ().fp_trans ()); + h = m_vp_trans.ctrans (text.size () > 0 ? text.size () : m_default_text_size); + } else { + h = m_vp_trans.ctrans (m_default_text_size); + } + + db::HAlign halign = text.halign (); + db::VAlign valign = text.valign (); + + double fy = 0.0; + if (valign == db::VAlignBottom || valign == db::NoVAlign) { + fy = 1.0; + } else if (valign == db::VAlignTop) { + fy = -1.0; + } + + double fx = 0.0; + if (halign == db::HAlignLeft || halign == db::NoHAlign) { + fx = 1.0; + } else if (halign == db::HAlignRight) { + fx = -1.0; + } + + db::DVector tp1 (fx * offset, fy * offset + (fy - 1) * 0.5 * h); + db::DVector tp2 (fx * offset, fy * offset + (fy + 1) * 0.5 * h); + db::DPoint dp = db::DPoint () + text.trans ().disp (); + + db::DBox b (dp + fp (tp1), dp + fp (tp2)); + + if (font == db::DefaultFont) { + + const lay::FixedFont &ff = lay::FixedFont::get_font (m_resolution); + + // count the lines + + unsigned int lines = 1; + for (const char *cp = text.string (); *cp; ) { + if (tl::skip_newline (cp)) { + ++lines; + } else { + tl::utf32_from_utf8 (cp); + } + } + + // compute the actual top left position + double ytop; + if (valign == db::VAlignBottom || valign == db::NoVAlign) { + ytop = b.bottom (); + ytop += double (ff.line_height () * (lines - 1) + ff.height ()); + } else if (valign == db::VAlignCenter) { + ytop = b.center ().y (); + ytop += double ((ff.line_height () * (lines - 1) + ff.height ()) / 2); + } else { + ytop = b.top (); + } + + // compute the bottom position + double ybottom = ytop - ff.line_height () * (lines - 1); + + // left and right position + bool first = false; + double xleft = 0.0, xright = 0.0; + + const char *cp = text.string (); + while (*cp) { + + unsigned int length = 0; + while (*cp && !tl::skip_newline (cp)) { + tl::utf32_from_utf8 (cp); + ++length; + } + + double xl; + if (halign == db::HAlignRight) { + xl = b.right (); + xl -= double (ff.width () * length); + } else if (halign == db::HAlignCenter) { + xl = b.center ().x (); + xl -= double (ff.width () * length / 2); + } else { + xl = b.left (); + } + xl -= 0.5; + + double xr = xl + double (ff.width () * length); + + if (first || xl < xleft) { + xleft = xl; + } + if (first || xr > xright) { + xright = xr; + } + first = false; + + } + + return db::DBox (xleft, ybottom, xright, ytop).transformed (m_vp_trans.inverted ()); + + } else { + + db::DHershey ht (text.string (), font); + ht.justify (b.transformed (m_vp_trans.inverted ()), halign, valign); + return ht.bbox (); + + } +} + +} diff --git a/src/laybasic/laybasic/layTextInfo.h b/src/laybasic/laybasic/layTextInfo.h new file mode 100644 index 000000000..ce700f89c --- /dev/null +++ b/src/laybasic/laybasic/layTextInfo.h @@ -0,0 +1,89 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2023 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#ifndef HDR_layTextInfo +#define HDR_layTextInfo + +#include "laybasicCommon.h" + +#include "dbText.h" +#include "dbBox.h" + +namespace lay +{ + +class LayoutViewBase; + +/** + * @brief A class providing information about a text's visual bounding box + * + * The class can act as a BoxConverter. + */ +class LAYBASIC_PUBLIC TextInfo +{ +public: + /** + * @brief Constructor + * + * @param view The LayoutView from which to take the text display parameters + * @param vp_trans The effective micron-to-pixel transformation + */ + TextInfo (const LayoutViewBase *view, const db::DCplxTrans &vp_trans); + + /** + * @brief Constructor + * + * @param default_text_size The default text size in micron + * @param default_font The default font + * @param apply_text_trans True if text transformations are to be applied + * @param resolution The resolution value (logical pixel size per physical unit pixel) + * @param vp_trans The effective micron-to-pixel transformation + */ + TextInfo (double default_text_size, const db::Font &default_font, bool apply_text_trans, double resolution, const db::DCplxTrans &vp_trans); + + /** + * @brief Gets the visual bounding box of the given DText object + * + * The visual bounding box is returned in micrometer units. + * It encloses the glyphs of the text, taking into account the + * text view settings by the view. + */ + db::DBox operator() (const db::DText &text) const + { + return get_bbox (text); + } + +private: + double m_default_text_size; + db::Font m_default_font; + bool m_apply_text_trans; + double m_resolution; + db::DCplxTrans m_vp_trans; + + db::DBox get_bbox (const db::DText &text) const; +}; + +} + +#endif + diff --git a/src/laybasic/laybasic/laybasic.pro b/src/laybasic/laybasic/laybasic.pro index f3b530330..bcd01c6d1 100644 --- a/src/laybasic/laybasic/laybasic.pro +++ b/src/laybasic/laybasic/laybasic.pro @@ -52,6 +52,7 @@ SOURCES += \ layEditable.cc \ layEditorServiceBase.cc \ layFinder.cc \ + layTextInfo.cc \ layFixedFont.cc \ layLayoutCanvas.cc \ layLineStylePalette.cc \ @@ -104,6 +105,7 @@ HEADERS += \ layEditorServiceBase.h \ layLayoutCanvas.h \ layFinder.h \ + layTextInfo.h \ layFixedFont.h \ layLayoutViewBase.h \ layLineStylePalette.h \ diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index bf3c32225..90758df1c 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -210,6 +210,22 @@ inline bool safe_isspace (char c) return c != 0 && static_cast (c) < 0x80 && isspace (c); } +// ------------------------------------------------------------------------- +// Utility: skip a newline + +bool skip_newline (const char *&cp) +{ + if (*cp == '\012' || *cp == '\015') { + if (*cp == '\015' && cp[1] == '\012') { + ++cp; + } + ++cp; + return true; + } else { + return false; + } +} + // ------------------------------------------------------------------------- // Utility: a strtod version that is independent of the locale diff --git a/src/tl/tl/tlString.h b/src/tl/tl/tlString.h index aa2f18490..33bc0413b 100644 --- a/src/tl/tl/tlString.h +++ b/src/tl/tl/tlString.h @@ -987,6 +987,37 @@ TL_PUBLIC uint32_t utf32_upcase (uint32_t c32); */ TL_PUBLIC uint32_t utf32_from_utf8 (const char *&cp, const char *cpe = 0); +/** + * @brief Checks if the next characters are CR, LF or CR+LF and skips them + * + * This function returns true, if a line separated was found and skipped + */ +TL_PUBLIC bool skip_newline (const char *&cp); + +/** + * @brief checks if the given character is a CR character + */ +inline bool is_cr (char c) +{ + return c == '\015'; +} + +/** + * @brief checks if the given character is a LF character + */ +inline bool is_lf (char c) +{ + return c == '\012'; +} + +/** + * @brief checks if the given character is a CR or LF character + */ +inline bool is_newline (char c) +{ + return is_cr (c) || is_lf (c); +} + } // namespace tl #endif