From 58e1d4f13d0b939a8ea243512be62f88df085450 Mon Sep 17 00:00:00 2001 From: olivetthered <37796246+olivetthered@users.noreply.github.com> Date: Tue, 9 Jun 2020 11:06:57 +0100 Subject: [PATCH 01/13] initial codse cleanup and fixes following first review --- scribus/plugins/import/pdf/slaoutput.cpp | 529 ++++++++++------------- 1 file changed, 239 insertions(+), 290 deletions(-) diff --git a/scribus/plugins/import/pdf/slaoutput.cpp b/scribus/plugins/import/pdf/slaoutput.cpp index ecc17c487..f7a3ee427 100644 --- a/scribus/plugins/import/pdf/slaoutput.cpp +++ b/scribus/plugins/import/pdf/slaoutput.cpp @@ -286,14 +286,8 @@ SlaOutputDev::SlaOutputDev(ScribusDoc* doc, QList *Elements, QStringL layersSetByOCG = false; importTextAsVectors = true; - m_textRegions.push_back(activeTextRegion); - //have a map of reusable addchar implementations instead of creating and deleting them all the time. - addCharModes[ADDCHARMODE::ADDFIRSTCHAR] = new AddFirstChar(this); - addCharModes[ADDCHARMODE::ADDBASICCHAR] = new AddBasicChar(this); - addCharModes[ADDCHARMODE::ADDCHARWITHNEWSTYLE] = new AddCharWithNewStyle(this); - addCharModes[ADDCHARMODE::ADDCHARWITHPREVIOUSSTYLE] = new AddCharWithPreviousStyle(this); - - addChar = addCharModes[ADDCHARMODE::ADDFIRSTCHAR]; + m_textFramework = new TextFramework(); + m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::ADDFIRSTCHAR]; } SlaOutputDev::~SlaOutputDev() @@ -302,11 +296,7 @@ SlaOutputDev::~SlaOutputDev() tmpSel->clear(); delete tmpSel; delete m_fontEngine; - //FIXME: could probably enumberate this - delete addCharModes[ADDCHARMODE::ADDFIRSTCHAR]; - delete addCharModes[ADDCHARMODE::ADDBASICCHAR]; - delete addCharModes[ADDCHARMODE::ADDCHARWITHNEWSTYLE]; - delete addCharModes[ADDCHARMODE::ADDCHARWITHPREVIOUSSTYLE]; + delete m_textFramework; } /* get Actions not implemented by Poppler */ @@ -3330,7 +3320,8 @@ void SlaOutputDev::drawChar(GfxState* state, double x, double y, double dx, doub FPointArray textPath; textPath.fromQPainterPath(qPath); FPoint wh = textPath.widthHeight(); - if (importTextAsVectors) { + if (importTextAsVectors) + { //qDebug() << "drawChar() "; if (textRenderingMode > 3) @@ -3371,7 +3362,7 @@ void SlaOutputDev::drawChar(GfxState* state, double x, double y, double dx, doub } } if (!importTextAsVectors) { // donm't render the char as vectors add it to an array so it can be rendred as a string - addChar->addChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + m_textFramework->addChar->addChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); } } } @@ -3444,30 +3435,27 @@ void SlaOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, do void SlaOutputDev::beginTextObject(GfxState *state) { pushGroup(); - if (importTextAsVectors == false && activeTextRegion.lastXY != QPointF(-1, -1)) { - activeTextRegion = TextRegion(); - m_textRegions.push_back(activeTextRegion); - } - else if (activeTextRegion.lastXY == QPointF(-1, -1)) { - qDebug("FIXME:Rogue _lastXY"); + if (importTextAsVectors == false && !m_textFramework->activeTextRegion.textRegionLines.empty()) { + m_textFramework->addNewTextRegion(); } - addChar = addCharModes[ADDFIRSTCHAR]; } void SlaOutputDev::endTextObject(GfxState *state) { - if (importTextAsVectors == false && activeTextRegion.lastXY != QPointF(-1, -1)) { + if (importTextAsVectors == false && !m_textFramework->activeTextRegion.textRegionLines.empty()) { // Add the last glyph to the textregion - if (activeTextRegion.addGlyphAtPoint(activeTextRegion.glyphs.back().position, activeTextRegion.glyphs.back()) == TextRegion::FAIL) { + QPointF glyphXY = m_textFramework->activeTextRegion.lastXY; + m_textFramework->activeTextRegion.lastXY.setX(m_textFramework->activeTextRegion.lastXY.x() - m_textFramework->activeTextRegion.glyphs.back().dx); + if (m_textFramework->activeTextRegion.addGlyphAtPoint(glyphXY, m_textFramework->activeTextRegion.glyphs.back()) == TextRegion::FAIL) { qDebug("FIXME: Rogue glyph detected, this should never happen because the copuror should move before glyphs in new regions are added."); } renderTextFrame(); - } else if (activeTextRegion.lastXY == QPointF(-1, -1)) { - qDebug("FIXME:Rogue _lastXY"); + } else if (importTextAsVectors == false && !m_textFramework->activeTextRegion.textRegionLines.empty()) { + qDebug("FIXME:Rogue textblock"); } - addChar = addCharModes[ADDFIRSTCHAR]; + m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::ADDFIRSTCHAR]; // qDebug() << "SlaOutputDev::endTextObject"; if (!m_clipTextPath.isEmpty()) { @@ -3939,56 +3927,66 @@ bool SlaOutputDev::checkClip() // TODO: remove mutual dependencies and move the render engine into it's own cc and header files because it's getting hard to navigate the code base. // PDF never deviates from the line when it comes to colenear -bool TextRegion::coLinera(qreal a, qreal b) { +bool TextRegion::coLinera(qreal a, qreal b) +{ return abs(a - b) < 1 ? true : false; } // like _colenia but we allow a deviation of upto +-2 rejion font text widths -bool TextRegion::closeToX(qreal x1, qreal x2){ +bool TextRegion::closeToX(qreal x1, qreal x2) +{ //TODO: return abs(x1 - x2) <= coreText.mWidth() * 2 ? true : false; - // return x1 - x2 <= modeHeigth ? true : false; //allow infinate overrun but only one char width underrun - return true; + return abs(x2 - x1) <= lineSpacing * 6 ? true : abs(x1 - this->textRegioBasenOrigin.x()) <= lineSpacing ? true: false; //allow infinate overrun but only one char width underrun + //return true; } // like _colenia but we allow a deviation of upto 2 rejion font linespaces, but in one direction and half a line space in the other direction -bool TextRegion::closeToY(qreal y1, qreal y2) { - //FIXME: Actually test the correctg magnitudes not the abs value. There shound be a parameter in the ui to set the matching tollerance - return (y2 - y1) >= 0 && y2 - y1 <= lineSpacing * 3 ? true : lineSpacing==-1 ? (y2 - y1) >= 0 && y2 - y1 <= modeHeigth * 5 ? true : false :false; +bool TextRegion::closeToY(qreal y1, qreal y2) +{ + //FIXME: Actually test the correctg magnitudes not the abs value. There shound be a parameter in the ui to set the matching tollerance but hard code to allow 3 linespaces difference before we say that the y is out of scope. + return (y2 - y1) >= 0 && y2 - y1 <= lineSpacing * 3 ? true : false; } // lesss than the last y value but bot more than the line spacing less, could also use the base line of the last line to be more accurate -bool TextRegion::adjunctLesser(qreal testY, qreal lastY, qreal baseY) { +bool TextRegion::adjunctLesser(qreal testY, qreal lastY, qreal baseY) +{ return (testY > lastY && testY <= baseY + lineSpacing - && lastY <= baseY + lineSpacing - && lineSpacing != -1) ? true : false; + && lastY <= baseY + lineSpacing); } // lesss than the last y value but bot more than the line spacing less, could also use the base line of the last line to be more accurate -bool TextRegion::adjunctGreater(qreal testY, qreal lastY, qreal baseY) { - return (testY < lastY - && lastY <= baseY - lineSpacing *0.75 - && lineSpacing != -1) ? true : false; +bool TextRegion::adjunctGreater(qreal testY, qreal lastY, qreal baseY) +{ + return (testY <= lastY + && testY >= baseY - lineSpacing * 0.75 + && lastY != baseY); } -TextRegion::FRAMEWORKLINETESTS TextRegion::linearTest(QPointF point, bool xInLimits, bool yInLimits) { +TextRegion::FRAMEWORKLINETESTS TextRegion::linearTest(QPointF point, bool xInLimits, bool yInLimits) +{ //TODO: add a FIRSTPOINT result as well FRAMEWORKLINETESTS pass = FAIL; + //FIXME: I think thjis should be using baseXY not lastXY if (coLinera(point.y(), lastXY.y())) { if (coLinera(point.x(), lastXY.x())) { pass = FIRSTPOINT; +#ifdef DEBUG_TEXT_IMPORT qDebug() << "FIRSTPOINT"; - }else // see if we are continuing along a line or if we can add a new line to take into account this first line may have truncated early, leaving the rest of the lines dangling out x's - if (xInLimits) +#endif + } // see if we are continuing along a line or if we can add a new line to take into account this first line may have truncated early, leaving the rest of the lines dangling out x's + else if (xInLimits) { // this is for item ##16 // ok, this should only happen when a new glyph is added not when the cursor position is set, but in both cases we can call extend by the point and set the glyph to the current glyph checking that it's not a duplicate //TODO: textRegionLines.end().extend(point).setGlyph(newGlyph); pass = SAMELINE; +#ifdef DEBUG_TEXT_IMPORT qDebug() << "SAMELINE " << point << " lastxy:"<< lastXY; +#endif } } // else see if y is a bit too much off thelastyx line to be linear else if (adjunctLesser(point.y(), lastXY.y(), lineBaseXY.y())) @@ -3997,39 +3995,52 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::linearTest(QPointF point, bool xInLim pass = STYLESUPERSCRIPT; qDebug() << "STYLESUPERSCRIPT point:" << point << " lastXY:" << lastXY << " lineBaseXY:" << lineBaseXY; } - else if (adjunctGreater(point.y(), lastXY.y(), lineBaseXY.y())) + else if (adjunctGreater(point.y(),lastXY.y(),lineBaseXY.y())) { if (coLinera(point.y(), lineBaseXY.y())) //PDF never deviates from the line when it comes to colenear { // were back on track pass = STYLENORMALRETURN; - //qDebug() << "STYLENORMALRETURN"; + qDebug() << "STYLENORMALRETURN"; } - else { + else + { //TODO: this character has overflowed the height, or is still superscript just not so much pass = STYLESUPERSCRIPT; //could be STYLEBELOWBASELINE - qDebug() << "STYLESUPERSCRIPT"; + qDebug() << "STYLESUBSCRIPT point:" << point << " lastXY:" << lastXY << " lineBaseXY:" << lineBaseXY; + //qDebug() << "STYLESUBSCRIPT: "; } } else { //TODO: We need to calculate things like new parargraphs and left hand justification - if (closeToX(textRegioBasenOrigin.x(), point.x())) + if (closeToX(point.x(), textRegioBasenOrigin.x())) { - if (closeToY(point.y(), lastXY.y())) { + if (closeToY(point.y(), lastXY.y()) && !coLinera(point.y(), lastXY.y())) { //TODO: We need to calculate things like new parargraphs and left hand justification - if ((textRegionLines.size() >= 2) && closeToX(textRegionLines[textRegionLines.size() - 2].width, maxWidth)) + if ((textRegionLines.size() >= 2)) //TODO: Need to setup some parameters replating to width matching, they mainly relate to justfication && closeToX(textRegionLines[textRegionLines.size() - 2].baseOrigin.x() + textRegionLines[textRegionLines.size() - 2].width, maxWidth)) { //TODO: add a new line and update the deltas pass = NEWLINE; - qDebug() << "NEWLINE1 point:" << point << " _lastXY:" << lastXY << " origin: " << textRegioBasenOrigin << " modeheight: " << modeHeigth << " this:" << this << " linespacing: " << lineSpacing; +#ifdef DEBUG_TEXT_IMPORT + qDebug() << "NEWLINE1 point:" << point << " _lastXY:" << lastXY << " origin: " << textRegioBasenOrigin << " this:" << this << " linespacing: " << lineSpacing; + +#endif // DEBUG } // we only have the first line so far, so pass without much of a test. else if (textRegionLines.size() == 1) { pass = NEWLINE; - qDebug() << "NEWLINE2 point:" << point << " _lastXY:" << lastXY << " origin: " << textRegioBasenOrigin << " modeheight: " << modeHeigth << " this:" << this << " linespacing: " << lineSpacing; +#ifdef DEBUG_TEXT_IMPORT + qDebug() << "NEWLINE2 point:" << point << " _lastXY:" << lastXY << " origin: " << textRegioBasenOrigin << " this:" << this << " linespacing: " << lineSpacing; +#endif } } } + else + { +#ifdef DEBUG_TEXT_IMPORT + qDebug() << "NEWLINE2 oops:"<glyphIndex != -1) + if (pass == FIRSTPOINT) { + if (textRegionLines.empty()) { + textRegionLines.push_back(TextRegionLine()); + } + } + else { textRegionLines.push_back(TextRegionLine()); - textRegionLine = &textRegionLines.back(); } - } - else { - //qDebug() << "NewSegment: "; - if (textRegionLine->segments.empty() || textRegionLine->segments.back().glyphIndex != -1) + textRegionLine = &textRegionLines.back(); + textRegionLine->baseOrigin = newPoint; + if (pass == NEWLINE) { - textRegionLine->segments.push_back(TextRegionLine()); + textRegionLine->maxHeight = abs(newPoint.y() - lastXY.y()); + if (textRegionLines.size() == 2) + { + lineSpacing = abs(newPoint.y() - lastXY.y()) + 1; + //qDebug() << "setting lineSpacing to:" << lineSpacing; + } } - textRegionLine = &textRegionLine->segments.back(); } - // we alweays need to set thease defaults if were adding - - textRegionLine->baseOrigin = newPoint; - //qDebug() << "base olrigin: " << textRegionLine->baseOrigin; - textRegionLine->width = 0; - // this should be - if (pass == NEWLINE) + textRegionLine = &textRegionLines.back(); + if ((pass == FIRSTPOINT && textRegionLine->segments.empty()) || pass == NEWLINE || pass != FIRSTPOINT && textRegionLine->segments[0].glyphIndex != textRegionLine->glyphIndex) { - //FIXME: I don't think it should be conditional I think it should just sets it. - textRegionLine->maxHeight = abs(newPoint.y() - lastXY.y()) > textRegionLine->maxHeight ? abs(newPoint.y() - lastXY.y()) : textRegionLine->maxHeight; - if(abs(lastXY.y() - newPoint.y()) + 1 > lineSpacing) - { - if (textRegionLines.size() == 2) { - lineSpacing = abs(newPoint.y() - lastXY.y()) + 1; - //qDebug() << "setting modeHeight to:" << modeHeigth; - } - } + TextRegionLine newSegment = TextRegionLine(); + textRegionLine->segments.push_back(newSegment); } - else if (pass == SAMELINE || pass == ENDOFLINE || pass == STYLENORMALRETURN) + TextRegionLine* segment = &textRegionLine->segments.back(); + segment->baseOrigin = newPoint; + if (pass == STYLESUPERSCRIPT) { - textRegionLine->maxHeight = textRegionLines.back().maxHeight > textRegionLine->maxHeight ? textRegionLines.back().maxHeight : textRegionLine->maxHeight; + segment->maxHeight = abs(lineSpacing - (newPoint.y() - lastXY.y())); + } else { - textRegionLine->maxHeight = abs(newPoint.y() - lastXY.y()) > textRegionLine->maxHeight ? abs(newPoint.y() - lastXY.y()) : textRegionLine->maxHeight; + segment->maxHeight = textRegionLines.back().maxHeight; } - maxHeight = abs(textRegioBasenOrigin.y() - newPoint.y()) > maxHeight ? abs(textRegioBasenOrigin.y() - newPoint.y()) : maxHeight; - //FIXME: this should really be the most common height accross all segments and lines. - //textRegionLine->modeHeigth = abs(newPoint.y() - lastXY.y()) > modeHeigth ? abs(newPoint.y() - lastXY.y()) : modeHeigth; - - //TODO: set modeHeigth - // check to see if were a new line or the first line and we have already had a character set on this line - if ((pass == FIRSTPOINT && textRegionLines.size() == 0) || (pass == NEWLINE && (textRegionLines.back().glyphIndex != -1))) { - //FIXME: Therre's a bug here because this will be a duplicate if it's set by thew pass == NEWLINE condition above - textRegionLines.push_back(*textRegionLine); - } // Otherwise see if were adding a new segment, changes is styles of the text etc... should trigger addint a new character so the glyphindex should be gaurenteed to be set if were adding a new segment - else if (textRegionLines.back().glyphIndex != -1 && pass == SAMELINE) { - if (textRegionLines.back().segments.size() > 0) { - textRegionLines.back().segments.back().width = abs(textRegionLines.back().segments.back().baseOrigin.x() - newPoint.x()); - } - //textRegionLines.back().segments.push_back(*textRegionLine); + + if (pass != NEWLINE && pass != FIRSTPOINT) + { + textRegionLines.back().segments.back().width = abs(textRegionLines.back().segments.back().baseOrigin.x() - newPoint.x()); textRegionLine = &textRegionLines.back(); textRegionLine->width = abs(textRegionLine->baseOrigin.x() - newPoint.x()); } + + maxHeight = abs(textRegioBasenOrigin.y() - newPoint.y()) > maxHeight ? abs(textRegioBasenOrigin.y() - newPoint.y()) : maxHeight; lastXY = newPoint; } else @@ -4182,100 +4171,67 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::moveToPoint(QPointF newPoint) } //TODO:, extract some font heights instesad of using dx all the time -TextRegion::FRAMEWORKLINETESTS TextRegion::addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph new_glyph) { - - QPointF movedGlyphPoint = QPointF(newGlyphPoint.x() + new_glyph.dx, newGlyphPoint.y() + new_glyph.dy); - //TODO: should probably be more forgiving when adding a glyph in the x direction because it could be several white spaces skipped +TextRegion::FRAMEWORKLINETESTS TextRegion::addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph newGlyph) +{ + QPointF movedGlyphPoint = QPointF(newGlyphPoint.x() + newGlyph.dx, newGlyphPoint.y() + newGlyph.dy); //qDebug() << "addGlyphAtPoint start" << newGlyphPoint << " glyph:"<< new_glyph.code; + //FIXME: There should be no need for testing the scope when adding a new glyph because moveto should have been called first but leave it in for now to catch any errors in the logic if (glyphs.size() == 1) { // FIXME: do a propper lookup of the height - modeHeigth = new_glyph.dx; + lineSpacing = newGlyph.dx * 2; //qDebug() << "addGlyphAtPoint start"; - } - if (lastXY == QPointF(-1, -1)) { lastXY = newGlyphPoint; - } - if (lineBaseXY == QPointF(-1, -1)) { lineBaseXY = newGlyphPoint; } bool xInLimits = false; - if (closeToX(newGlyphPoint.x(), lastXY.x())) { + if (closeToX(newGlyphPoint.x(), lastXY.x())) + { xInLimits = true; } bool yInLimits = false; - if (closeToY(newGlyphPoint.y(), lastXY.y())) { + if (closeToY(newGlyphPoint.y(), lastXY.y())) + { yInLimits = true; } FRAMEWORKLINETESTS pass = linearTest(newGlyphPoint, xInLimits, yInLimits); - //TODO: Implement this in the caller - // if nothing can be done then write out the textregioin and delete it and create a new trextrext and re-adcfd ther char, set the pos etc... - if (pass != FRAMEWORKLINETESTS::FAIL) { - //TODO: possible don't store the glyphs on the textRegion, well certainly not like this, we only need to check bounbdry and feature positions - //glyphs.push_back(new_glyph); - maxHeight = abs(textRegioBasenOrigin.y() - movedGlyphPoint.y()) + modeHeigth > maxHeight ? abs(textRegioBasenOrigin.y() - movedGlyphPoint.y()) + modeHeigth : maxHeight; - /* - if (textRegionLines.size() == 0) - { - TextRegionLine newTextRegionLine = TextRegionLine(); - newTextRegionLine.glyphIndex = glyphs.size() - 1; - newTextRegionLine.width = new_glyph.dx; - textRegionLines.push_back(newTextRegionLine); - } - */ - TextRegionLine* textRegionLine = nullptr; - //at the moment a new segment only gets added when the line is created. a new segment should also get added if there's any change in style or layout etc...but that feature can be added llater, it's not needed for basic textframe support with no style. - // FIXME: becaused moveTo should always be called first we should always have a textRegionLines - if (textRegionLines.size() == 0) - { - TextRegionLine newTextRegionLine = TextRegionLine(); - newTextRegionLine.baseOrigin = QPointF(textRegioBasenOrigin.x(), newGlyphPoint.y()); - textRegionLines.push_back(newTextRegionLine); - } - textRegionLine = &textRegionLines.back(); + if (pass != FRAMEWORKLINETESTS::FAIL) + { + maxHeight = abs(textRegioBasenOrigin.y() - movedGlyphPoint.y()) + lineSpacing > maxHeight ? abs(textRegioBasenOrigin.y() - movedGlyphPoint.y()) + lineSpacing : maxHeight; + //moveto deals with setting newlines and segments, all we hve to do is populatit them with the parameters tyhe glyph gives us such as it's width and height and set the glyph index for the newlines and segments - if (textRegionLine->glyphIndex == -1) - { + TextRegionLine* textRegionLine = &textRegionLines.back(); + if (pass == NEWLINE || pass == FIRSTPOINT) { textRegionLine->glyphIndex = glyphs.size() - 1; textRegionLine->baseOrigin = QPointF(textRegioBasenOrigin.x(), newGlyphPoint.y()); } - - if (textRegionLine->segments.empty() || newGlyphPoint.x() > textRegionLine->baseOrigin.x() + textRegionLine->width && textRegionLine->segments.back().glyphIndex != -1) - { - // add a new segment - TextRegionLine newSegment = TextRegionLine(); - newSegment.glyphIndex = textRegionLine->glyphIndex; - newSegment.width = 0; - newSegment.baseOrigin = QPointF(newGlyphPoint.x(), textRegionLine->baseOrigin.y()); - textRegionLine->segments.push_back(newSegment); - } - // update the text line and segment widths, + TextRegionLine *segment = &textRegionLine->segments.back(); segment->width = abs(movedGlyphPoint.x() - segment->baseOrigin.x()); segment->glyphIndex = glyphs.size() - 1; + qreal thisHeight = 0; if (textRegionLines.size() > 1) { - segment->modeHeigth = abs(newGlyphPoint.y() - textRegionLines[textRegionLines.size() - 2].baseOrigin.y()); - segment->maxHeight = segment->modeHeigth > segment->maxHeight ? segment->modeHeigth : segment->maxHeight; + thisHeight = abs(newGlyphPoint.y() - textRegionLines[textRegionLines.size() - 2].baseOrigin.y()); } else { - segment->modeHeigth = new_glyph.dx; - segment->maxHeight = segment->modeHeigth > segment->maxHeight ? segment->modeHeigth : segment->maxHeight; + thisHeight = newGlyph.dx; } - textRegionLine->maxHeight = textRegionLine->maxHeight > segment->maxHeight ? textRegionLine->maxHeight : segment->maxHeight; - textRegionLine->modeHeigth = textRegionLine->maxHeight; //FIXME: this needs to be calculated based on the heights of all the segments + segment->maxHeight = thisHeight > segment->maxHeight ? thisHeight : segment->maxHeight; + textRegionLine->maxHeight = textRegionLine->maxHeight > thisHeight ? textRegionLine->maxHeight : thisHeight; textRegionLine->width = abs(movedGlyphPoint.x() - textRegionLine->baseOrigin.x()); + maxWidth = textRegionLine->width > maxWidth ? textRegionLine->width : maxWidth; - - //FIXME: Check if we are on a new line before setting _lineBaseXY - lineBaseXY = textRegionLine->baseOrigin;// movedGlyphPoint; + if (textRegionLine->segments.size() == 1) + { + lineBaseXY = textRegionLine->baseOrigin; + } lastXY = movedGlyphPoint; - } else { - qDebug() << "addGlyphAtPoint failed"; + qDebug() << "addGlyphAtPoint failed, this should never happen"; } return pass; } @@ -4285,14 +4241,22 @@ void TextRegion::renderToTextFrame(PageItem* textNode, ParagraphStyle& pStyle) // nothing clever, just get all the body text in one lump and update the text frame textNode->setWidthHeight(this->maxWidth, this->maxHeight); QString bodyText = ""; - for (int glyphIndex = this->textRegionLines.begin()->glyphIndex; glyphIndex <= this->textRegionLines.back().segments.back().glyphIndex; glyphIndex++) { + for (int glyphIndex = this->textRegionLines.begin()->glyphIndex; glyphIndex <= this->textRegionLines.back().segments.back().glyphIndex; glyphIndex++) + { bodyText += glyphs[glyphIndex].code; } textNode->itemText.insertChars(bodyText); textNode->frameTextEnd(); } -void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) { +bool TextRegion::isNew() +{ + return textRegionLines.empty() || + glyphs.empty(); +} + +void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) +{ textNode->ClipEdited = true; textNode->FrameType = 3; @@ -4303,7 +4267,9 @@ void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) { int textRenderingMode = state->getRender(); // Invisible or only used for clipping if (textRenderingMode == 3) + { return; + } // Fill text rendering modes. See above if (textRenderingMode == 0 || textRenderingMode == 2 || textRenderingMode == 4 || textRenderingMode == 6) { @@ -4317,7 +4283,8 @@ void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) { textNode->setLineWidth(0);//line width doesn't effect drawing text, it creates a bounding box state->getTransformedLineWidth()); textNode->setFillShade(CurrFillShade); } - else { + else + { textNode->setFillColor(CurrColorFill); textNode->setFillShade(CurrFillShade); textNode->setFillEvenOdd(false); @@ -4338,7 +4305,8 @@ void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) { textNode->setFillBlendmode(getBlendMode(state)); textNode->setFillShade(CurrFillShade); } - else { + else + { textNode->setLineColor(CurrColorStroke); textNode->setLineWidth(0);//line width doesn't effect drawing text, it creates a bounding box state->getTransformedLineWidth()); textNode->setFillTransparency(1.0 - state->getFillOpacity() > state->getStrokeOpacity() ? state->getFillOpacity() : state->getStrokeOpacity()); @@ -4352,85 +4320,53 @@ void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) { /** * \brief Updates current text position */ -void SlaOutputDev::updateTextPos(GfxState* state) { - QPointF new_position = QPointF(state->getCurX(), state->getCurY()); - //qDebug() << "updateTextPos() new_position: " << new_position << " lastxy: " << activeTextRegion.lastXY << " lineBaseXY: " << activeTextRegion.lineBaseXY << " origin: " << activeTextRegion.textRegioBasenOrigin; - //check to see if we are in a new text region - if (activeTextRegion.textRegioBasenOrigin == QPointF(-1, -1) || activeTextRegion.textRegionLines.size() == 0 || - (activeTextRegion.textRegionLines.size() == 1 && activeTextRegion.textRegionLines.back().glyphIndex == -1) )// && !isRegionConcurrent(new_position))) - { - //FIXME: Actually put this in the correct class - activeTextRegion.textRegioBasenOrigin = new_position; - // this ahould really get picked up by add first glyph, so check if that happens and if it does remove this. also we only want to call for the very first glyph of a new region, not every glyph for the begining of every line. - // don't make an arbitrary call to addGlyphAtPoint, instead pick the glyph up via addFirstGlyph - // we catch end of line glyphs further down anyway. - addChar = addCharModes[ADDFIRSTCHAR]; -# /* FIXME: Do we need this here? is there a better test we can use before calling it, can't we just get it picked up by add first glyph - if (glyphs.size() > 0) - { - addGlyphAtPoint(glyphs.back().position, glyphs.back()); - } - */ +void SlaOutputDev::updateTextPos(GfxState* state) +{ + QPointF newPosition = QPointF(state->getCurX(), state->getCurY()); + TextRegion* activeTextRegion = &m_textFramework->activeTextRegion; + if ((activeTextRegion->isNew()) + ) + { + activeTextRegion->textRegioBasenOrigin = newPosition; + m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::ADDFIRSTCHAR]; } else { - // a delayed call using the last glyph that was put onto the stack. it will be a glyph situated on the far side bounds of the text region - // only add if we are on a new line, so the y position will be shifted but the glyph.y and textregion.y should marry - if (activeTextRegion.coLinera(activeTextRegion.lastXY.y(), activeTextRegion.glyphs.back().position.y()) && - !activeTextRegion.coLinera(new_position.y(), activeTextRegion.glyphs.back().position.y()) //&& - //activeTextRegion.closeToX(new_position.x(), activeTextRegion.glyphs.back().position.x()) - ) + // if we've will move to a new line or new text region then update the current text region with the last glyph, this ensures all textlines and textregions have terminating glyphs. + if (m_textFramework->isNewLineOrRegion(newPosition)) { - if (activeTextRegion.addGlyphAtPoint(activeTextRegion.glyphs.back().position, activeTextRegion.glyphs.back()) == TextRegion::FAIL) { - qDebug("FIXME: Rogue glyph detected, this should never happen because the copuror should move before glyphs in new regions are added."); - /* - // we have an out of place glyph being added. This shouldn't ever really happen as the cursor is always moved before glyphs are added - // still failsafe - // FIXME: This should be done when the document has finished loading so layout fixups can be put inplace - if (activeTextRegion.textRegionLines.size() > 0) - { - renderTextFrame(); - } - - //Create and initilize a new TextRegion - activeTextRegion = TextRegion(); - m_textRegions.push_back(activeTextRegion); - - //initialize m_activeTextRegion based on newGlyphPoint and new_glyph - activeTextRegion.addGlyphAtPoint(glyphs.back().position, glyphs.back()); - addChar = addCharModes[ADDBASICCHAR]; - */ + QPointF glyphPosition = activeTextRegion->lastXY; + activeTextRegion->lastXY.setX(activeTextRegion->lastXY.x() - activeTextRegion->glyphs.back().dx); + if (activeTextRegion->addGlyphAtPoint(glyphPosition, activeTextRegion->glyphs.back()) == TextRegion::FAIL) { + qDebug("FIXME: Rogue glyph detected, this should never happen because the cursor should move before glyphs in new regions are added."); + } + else + { + qDebug() << "Nedwline should be next"; }; } } - TextRegion::FRAMEWORKLINETESTS lineTestResult = activeTextRegion.moveToPoint(new_position); - if (lineTestResult == TextRegion::FAIL) { - //This should never happen now, I could pass the result out and have the caller implement creating a new text rewgion - - // FIXME: redner the textframe, this should be done after the document has finished loading so all the layout fixups can be put inplace first + TextRegion::FRAMEWORKLINETESTS lineTestResult = activeTextRegion->moveToPoint(newPosition); + if (lineTestResult == TextRegion::FAIL) + { + // FIXME: render the textframe, this should be done after the document has finished loading the current page so all the layout fixups can be put inplace first renderTextFrame(); //Create and initilize a new TextRegion - activeTextRegion = TextRegion(); - m_textRegions.push_back(activeTextRegion); - addChar = addCharModes[ADDFIRSTCHAR]; + m_textFramework->addNewTextRegion(); updateTextPos(state); - - /*TOPDO: DXo we need to initlize thease - m_activeTextRegion.textRegioBasenOrigin = newPoint; - m_activeTextRegion._lineBaseXY = newPoint; - m_activeTextRegion._lastXY = newPoint; - */ } } void SlaOutputDev::renderTextFrame() { - //TODO: Implement, this should all be based on the framework and using m_activeTextRegion + //TODO: Implement, this should all in based the framework //qDebug() << "_flushText() m_doc->currentPage()->xOffset():" << m_doc->currentPage()->xOffset(); // Ignore empty strings - if (activeTextRegion.glyphs.empty()) { + TextRegion* activeTextRegion = &m_textFramework->activeTextRegion; + if (activeTextRegion->glyphs.empty()) + { // We don't clear the glyphs any more or at least until the whole page has been rendred glyphs.clear(); return; } @@ -4447,10 +4383,10 @@ void SlaOutputDev::renderTextFrame() } */ //FIXME: Use the framework for positioning not the first glyph - qreal xCoor = m_doc->currentPage()->xOffset() + activeTextRegion.textRegioBasenOrigin.x(); - qreal yCoor = m_doc->currentPage()->initialHeight() - (m_doc->currentPage()->yOffset() + (double)activeTextRegion.textRegioBasenOrigin.y() + activeTextRegion.lineSpacing); // don't know if y is top down or bottom up + qreal xCoor = m_doc->currentPage()->xOffset() + activeTextRegion->textRegioBasenOrigin.x(); + qreal yCoor = m_doc->currentPage()->initialHeight() - (m_doc->currentPage()->yOffset() + (double)activeTextRegion->textRegioBasenOrigin.y() + activeTextRegion->lineSpacing); // don't know if y is top down or bottom up double lineWidth = 0.0; - qDebug() << "rendering new frame at:" << xCoor << "," << yCoor << " With lineheight of: " << activeTextRegion.modeHeigth << "Height:" << activeTextRegion.maxHeight << " Width:" << activeTextRegion.maxWidth; + qDebug() << "rendering new frame at:" << xCoor << "," << yCoor << " With lineheight of: " << activeTextRegion->lineSpacing << "Height:" << activeTextRegion->maxHeight << " Width:" << activeTextRegion->maxWidth; /* colours don't get reset to CommonStrings::None often enough.*/ int z = m_doc->itemAdd(PageItem::TextFrame, PageItem::Rectangle, xCoor, yCoor, 40, 40, 0, CommonStrings::None, CommonStrings::None /* this->CurrColorStroke*/);//, PageItem::ItemKind::InlineItem); PageItem* textNode = m_doc->Items->at(z); @@ -4460,7 +4396,6 @@ void SlaOutputDev::renderTextFrame() pStyle.setLineSpacingMode(pStyle.AutomaticLineSpacing); pStyle.setHyphenationMode(pStyle.AutomaticHyphenation); - // TODO: Implement thease using the framework finishItem(textNode); // FIXME: Implement thease using the framework @@ -4473,7 +4408,6 @@ void SlaOutputDev::renderTextFrame() textNode->setLineEnd(PLineEnd); textNode->setLineJoin(PLineJoin); textNode->setTextFlowMode(PageItem::TextFlowDisabled); - //textNode->setFillTransparency(1.0); textNode->setLineTransparency(1.0); // this ssets the transparency of the textbox border and we don't want to see it textNode->setFillColor(CommonStrings::None); @@ -4482,7 +4416,6 @@ void SlaOutputDev::renderTextFrame() textNode->setFillShade(CurrFillShade); } - // Set text matrix... This need to be done so that the globaal world view that we rite out glyphs to is transformed correctly by the context matrix for each glyph, possibly anyhow. /* FIXME: Setting the text matrix isn't supp;orted at the moment QTransform text_transform(_text_matrix); @@ -4509,7 +4442,7 @@ void SlaOutputDev::renderTextFrame() textNode->itemText.setDefaultStyle(pStyle); textNode->invalid = true; - activeTextRegion.renderToTextFrame(textNode, pStyle); + activeTextRegion->renderToTextFrame(textNode, pStyle); //FIXME: Paragraphs need to be implemented properly this needs to be applied to the charstyle of the default pstyle textNode->itemText.insertChars(SpecialChars::PARSEP, true); @@ -4552,15 +4485,14 @@ void SlaOutputDev::renderTextFrame() m_groupStack.top().Items.append(textNode); applyMask(textNode); } - } /*code mostly taken from importodg.cpp which also supports some line styles and more fill options etc...*/ //FIXME: This needs to be implemented based on the framework -void SlaOutputDev::finishItem(PageItem* item) { +void SlaOutputDev::finishItem(PageItem* item) +{ item->ClipEdited = true; item->FrameType = 3; - //this requires that PoLine is set //FPoint wh = getMaxClipF(&item->PoLine); //item->setWidthHeight(wh.x(), wh.y()); @@ -4569,7 +4501,6 @@ void SlaOutputDev::finishItem(PageItem* item) { item->OldH2 = item->height(); item->updateClip(); item->OwnPage = m_doc->OnPage(item); - //item->setFillTransparency(1.0 - state->getFillOpacity() > state->getStrokeOpacity() ? state->getFillOpacity() : state->getStrokeOpacity()); //item->setLineTransparency(1.0); } @@ -4578,61 +4509,45 @@ void SlaOutputDev::finishItem(PageItem* item) { void AddFirstChar::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { //qDebug() << "addChar() '" << u << " : " << uLen; - /* - bool is_space = (uLen == 1 && u[0] == 32); - // Skip beginning space - if (is_space) { - return; - } - */ - PdfGlyph new_glyph; - //new_glyph.is_space = false; - new_glyph.position = QPointF(x - originX, y - originY); - new_glyph.dx = dx; - new_glyph.dy = dy; + PdfGlyph newGlyph; + newGlyph.dx = dx; + newGlyph.dy = dy; - m_slaOutputDev->addChar = m_slaOutputDev->addCharModes[SlaOutputDev::ADDBASICCHAR]; + m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::ADDBASICCHAR]; // Convert the character to UTF-16 since that's our SVG document's encoding - for (int i = 0; i < uLen; i++) { - new_glyph.code += (char16_t)u[i]; + for (int i = 0; i < uLen; i++) + { + newGlyph.code = (char16_t)u[i]; } - new_glyph.rise = state->getRise(); - m_slaOutputDev->activeTextRegion.glyphs.push_back(new_glyph); + newGlyph.rise = state->getRise(); + //m_slaOutputDev->activeTextRegion.lastXY = QPointF(x, y); + m_textFramework->activeTextRegion.glyphs.push_back(newGlyph); //only need to be called for the very first point - if (m_slaOutputDev->activeTextRegion.addGlyphAtPoint(QPointF(x, y), new_glyph) == TextRegion::FAIL) { - qDebug("FIXME: Rogue glyph detected, this should never happen because the copuror should move before glyphs in new regions are added."); + if (m_textFramework->activeTextRegion.addGlyphAtPoint(QPointF(x, y), newGlyph) == TextRegion::FAIL) + { + qDebug("FIXME: Rogue glyph detected, this should never happen because the coursor should move before glyphs in new regions are added."); } } void AddBasicChar::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { //qDebug() << "addChar() '" << u << " : " << uLen; - // TODO: Compleatly gut this function so all that it ends up doing is placing a character and some positioning information on a stack get rid of all the other junk as it's not needed - /* - bool is_space = (uLen == 1 && u[0] == 32); - - // Allow only one space in a row - if (is_space && - (m_slaOutputDev->activeTextRegion.glyphs[m_slaOutputDev->activeTextRegion.glyphs.size() - 1].code == QChar::SpecialCharacter::Space)) { - return; - } - */ - PdfGlyph new_glyph; - //new_glyph.is_space = is_space; - new_glyph.position = QPoint(x - originX, y - originY); - new_glyph.dx = dx; - new_glyph.dy = dy; + PdfGlyph newGlyph; + newGlyph.dx = dx; + newGlyph.dy = dy; // Convert the character to UTF-16 since that's our SVG document's encoding - for (int i = 0; i < uLen; i++) { - new_glyph.code += (char16_t)u[i]; + for (int i = 0; i < uLen; i++) + { + newGlyph.code = (char16_t)u[i]; } - new_glyph.rise = state->getRise(); - m_slaOutputDev->activeTextRegion.glyphs.push_back(new_glyph); + newGlyph.rise = state->getRise(); + m_textFramework->activeTextRegion.lastXY = QPointF(x, y); + m_textFramework->activeTextRegion.glyphs.push_back(newGlyph); } void AddCharWithPreviousStyle::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) @@ -4642,3 +4557,37 @@ void AddCharWithPreviousStyle::addChar(GfxState* state, double x, double y, doub void AddCharWithNewStyle::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { } + +TextFramework::TextFramework() +{ + m_textRegions.push_back(activeTextRegion); + //have a map of reusable addchar implementations instead of creating and deleting them all the time. + addCharModes[ADDCHARMODE::ADDFIRSTCHAR] = new AddFirstChar(this); + addCharModes[ADDCHARMODE::ADDBASICCHAR] = new AddBasicChar(this); + addCharModes[ADDCHARMODE::ADDCHARWITHNEWSTYLE] = new AddCharWithNewStyle(this); + addCharModes[ADDCHARMODE::ADDCHARWITHPREVIOUSSTYLE] = new AddCharWithPreviousStyle(this); +} + +TextFramework::~TextFramework() +{ + //FIXME: could probably enumberate this + delete addCharModes[ADDCHARMODE::ADDFIRSTCHAR]; + delete addCharModes[ADDCHARMODE::ADDBASICCHAR]; + delete addCharModes[ADDCHARMODE::ADDCHARWITHNEWSTYLE]; + delete addCharModes[ADDCHARMODE::ADDCHARWITHPREVIOUSSTYLE]; +} + +void TextFramework::addNewTextRegion() +{ + activeTextRegion = TextRegion(); + m_textRegions.push_back(activeTextRegion); + addChar = addCharModes[TextFramework::ADDFIRSTCHAR]; +} + +bool TextFramework::isNewLineOrRegion(QPointF newPosition) +{ + return (activeTextRegion.coLinera(activeTextRegion.lastXY.y(), activeTextRegion.textRegionLines.back().baseOrigin.y()) && + !activeTextRegion.coLinera(newPosition.y(), activeTextRegion.lastXY.y())) + || (activeTextRegion.coLinera(newPosition.y(), activeTextRegion.lastXY.y()) + && !activeTextRegion.closeToX(newPosition.x(), activeTextRegion.lastXY.x())); +} From 873d9e6c030f41913657a948b0d1e6c5e2779e89 Mon Sep 17 00:00:00 2001 From: olivetthered <37796246+olivetthered@users.noreply.github.com> Date: Tue, 9 Jun 2020 11:07:01 +0100 Subject: [PATCH 02/13] Update slaoutput.h --- scribus/plugins/import/pdf/slaoutput.h | 96 +++++++++++++------------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/scribus/plugins/import/pdf/slaoutput.h b/scribus/plugins/import/pdf/slaoutput.h index 83821b668..948695375 100644 --- a/scribus/plugins/import/pdf/slaoutput.h +++ b/scribus/plugins/import/pdf/slaoutput.h @@ -159,23 +159,21 @@ class AnoOutputDev : public OutputDev * */ struct PdfGlyph { - QPointF position; // Absolute glyph coords double dx; // X advance value double dy; // Y advance value double rise; // Text rise parameter - QString code; // UTF-16 coded character but we only store and use UTF-8, the slternstive is const char * for utf8 so far as qt is concerned - bool is_space; + QChar code; // UTF-16 coded character }; class TextRegionLine { public: - qreal maxHeight = -1; - qreal modeHeigth = -1; - qreal width = -1; - int glyphIndex = -1; - QPointF baseOrigin = QPointF(-1, -1); + qreal maxHeight = 0; + //we can probably use maxHeight for this. + qreal width = 0; + int glyphIndex = 0; + QPointF baseOrigin = QPointF(0, 0); std::vector segments = std::vector(); }; @@ -193,14 +191,13 @@ class TextRegion { FAIL }; - QPointF textRegioBasenOrigin = QPointF(-1, -1); - qreal maxHeight = -1; - qreal modeHeigth = -1; - qreal lineSpacing = -1; + QPointF textRegioBasenOrigin = QPointF(0, 0); + qreal maxHeight = 0; + qreal lineSpacing = 1; std::vector textRegionLines = std::vector(); - qreal maxWidth = -1; - QPointF lineBaseXY = QPointF(-1, -1); //updated with the best match left value from all the textRegionLines and the best bottom value from the textRegionLines.segments; - QPointF lastXY = QPointF(-1, -1); + qreal maxWidth = 0; + QPointF lineBaseXY = QPointF(0, 0); //updated with the best match left value from all the textRegionLines and the best bottom value from the textRegionLines.segments; + QPointF lastXY = QPointF(0, 0); static bool coLinera(qreal a, qreal b); bool closeToX(qreal x1, qreal x2); bool closeToY(qreal y1, qreal y2); @@ -211,7 +208,8 @@ class TextRegion { TextRegion::FRAMEWORKLINETESTS moveToPoint(QPointF newPoint); TextRegion::FRAMEWORKLINETESTS addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph new_glyph); void renderToTextFrame(PageItem* textNode, ParagraphStyle& pStyle); - std::vector glyphs; //this may replace some of the other settings or it may not, certainly not font as text gets flushed if the font changes + std::vector glyphs; + bool isNew(); }; class AddCharInterface @@ -221,6 +219,26 @@ class AddCharInterface virtual void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) = 0; }; +class TextFramework +{ +public: + TextFramework(); + ~TextFramework(); + enum ADDCHARMODE { + ADDFIRSTCHAR, + ADDBASICCHAR, + ADDCHARWITHNEWSTYLE, + ADDCHARWITHPREVIOUSSTYLE + }; + std::map addCharModes; + TextRegion& activeTextRegion = TextRegion(); //faster than calling back on the vector all the time. + void addNewTextRegion(); + AddCharInterface* addChar = nullptr; + bool isNewLineOrRegion(QPointF newPosition); +private: + std::vector m_textRegions = std::vector(); +}; + class SlaOutputDev : public OutputDev { public: @@ -342,21 +360,6 @@ class SlaOutputDev : public OutputDev void type3D0(GfxState * /*state*/, double /*wx*/, double /*wy*/) override; void type3D1(GfxState * /*state*/, double /*wx*/, double /*wy*/, double /*llx*/, double /*lly*/, double /*urx*/, double /*ury*/) override; - //text as text - - AddCharInterface* addChar = nullptr; - - enum ADDCHARMODE { - ADDFIRSTCHAR, - ADDBASICCHAR, - ADDCHARWITHNEWSTYLE, - ADDCHARWITHPREVIOUSSTYLE - }; - - std::map addCharModes; - - TextRegion& activeTextRegion = TextRegion(); //faster than calling back on the vector all the time. - //----- form XObjects void drawForm(Ref /*id*/) override { qDebug() << "Draw Form"; } @@ -462,63 +465,58 @@ class SlaOutputDev : public OutputDev QHash > m_radioMap; QHash m_radioButtons; int m_actPage; - //PDF Textbox framework - std::vector m_textRegions = std::vector(); + TextFramework* m_textFramework = nullptr; }; class AddFirstChar : public AddCharInterface { - public: - AddFirstChar(SlaOutputDev *slaOutputDev) + AddFirstChar(TextFramework* textFramework) { - m_slaOutputDev = slaOutputDev; + m_textFramework = textFramework; } void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) override; private: - SlaOutputDev* m_slaOutputDev = nullptr; + TextFramework* m_textFramework = nullptr; }; class AddBasicChar : public AddCharInterface { - public: - AddBasicChar(SlaOutputDev* slaOutputDev) + AddBasicChar(TextFramework* textFramework) { - m_slaOutputDev = slaOutputDev; + m_textFramework = textFramework; } void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) override; private: - SlaOutputDev* m_slaOutputDev = nullptr; + TextFramework* m_textFramework = nullptr; }; // TODO: implement these addchar definitions so that they can handle changes in style, font, text micro positioning, scaling, matrix etc... class AddCharWithNewStyle : public AddCharInterface { - public: - AddCharWithNewStyle(SlaOutputDev* slaOutputDev) + AddCharWithNewStyle(TextFramework* textFramework) { - m_slaOutputDev = slaOutputDev; + m_textFramework = textFramework; } void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) override; private: - SlaOutputDev* m_slaOutputDev = nullptr; + TextFramework* m_textFramework = nullptr; }; // TODO: implement these addchar definitions so that they can handle changes in style, font, text micro positioning, scaling, matrix etc... class AddCharWithPreviousStyle : public AddCharInterface { - public: - AddCharWithPreviousStyle(SlaOutputDev* slaOutputDev) + AddCharWithPreviousStyle(TextFramework* textFramework) { - m_slaOutputDev = slaOutputDev; + m_textFramework = textFramework; } void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) override; private: - SlaOutputDev* m_slaOutputDev = nullptr; + TextFramework* m_textFramework = nullptr; }; #endif From d5cbdfed36be4584af4dde8aef84d870372bc01d Mon Sep 17 00:00:00 2001 From: olivetthered <37796246+olivetthered@users.noreply.github.com> Date: Tue, 9 Jun 2020 11:27:05 +0100 Subject: [PATCH 03/13] move the enums into their own class move the enums into their own class --- scribus/plugins/import/pdf/slaoutput.cpp | 84 ++++++++++++------------ scribus/plugins/import/pdf/slaoutput.h | 14 ++-- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/scribus/plugins/import/pdf/slaoutput.cpp b/scribus/plugins/import/pdf/slaoutput.cpp index f7a3ee427..230bdc5a5 100644 --- a/scribus/plugins/import/pdf/slaoutput.cpp +++ b/scribus/plugins/import/pdf/slaoutput.cpp @@ -287,7 +287,7 @@ SlaOutputDev::SlaOutputDev(ScribusDoc* doc, QList *Elements, QStringL importTextAsVectors = true; m_textFramework = new TextFramework(); - m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::ADDFIRSTCHAR]; + m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::AddCharMode::ADDFIRSTCHAR]; } SlaOutputDev::~SlaOutputDev() @@ -3447,7 +3447,7 @@ void SlaOutputDev::endTextObject(GfxState *state) // Add the last glyph to the textregion QPointF glyphXY = m_textFramework->activeTextRegion.lastXY; m_textFramework->activeTextRegion.lastXY.setX(m_textFramework->activeTextRegion.lastXY.x() - m_textFramework->activeTextRegion.glyphs.back().dx); - if (m_textFramework->activeTextRegion.addGlyphAtPoint(glyphXY, m_textFramework->activeTextRegion.glyphs.back()) == TextRegion::FAIL) { + if (m_textFramework->activeTextRegion.addGlyphAtPoint(glyphXY, m_textFramework->activeTextRegion.glyphs.back()) == TextRegion::FrameworkLineTests::FAIL) { qDebug("FIXME: Rogue glyph detected, this should never happen because the copuror should move before glyphs in new regions are added."); } renderTextFrame(); @@ -3455,7 +3455,7 @@ void SlaOutputDev::endTextObject(GfxState *state) qDebug("FIXME:Rogue textblock"); } - m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::ADDFIRSTCHAR]; + m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::AddCharMode::ADDFIRSTCHAR]; // qDebug() << "SlaOutputDev::endTextObject"; if (!m_clipTextPath.isEmpty()) { @@ -3963,17 +3963,17 @@ bool TextRegion::adjunctGreater(qreal testY, qreal lastY, qreal baseY) && lastY != baseY); } -TextRegion::FRAMEWORKLINETESTS TextRegion::linearTest(QPointF point, bool xInLimits, bool yInLimits) +TextRegion::FrameworkLineTests TextRegion::linearTest(QPointF point, bool xInLimits, bool yInLimits) { //TODO: add a FIRSTPOINT result as well - FRAMEWORKLINETESTS pass = FAIL; + FrameworkLineTests pass = FrameworkLineTests::FAIL; //FIXME: I think thjis should be using baseXY not lastXY if (coLinera(point.y(), lastXY.y())) { if (coLinera(point.x(), lastXY.x())) { - pass = FIRSTPOINT; + pass = FrameworkLineTests::FIRSTPOINT; #ifdef DEBUG_TEXT_IMPORT qDebug() << "FIRSTPOINT"; #endif @@ -3983,7 +3983,7 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::linearTest(QPointF point, bool xInLim // this is for item ##16 // ok, this should only happen when a new glyph is added not when the cursor position is set, but in both cases we can call extend by the point and set the glyph to the current glyph checking that it's not a duplicate //TODO: textRegionLines.end().extend(point).setGlyph(newGlyph); - pass = SAMELINE; + pass = FrameworkLineTests::SAMELINE; #ifdef DEBUG_TEXT_IMPORT qDebug() << "SAMELINE " << point << " lastxy:"<< lastXY; #endif @@ -3992,7 +3992,7 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::linearTest(QPointF point, bool xInLim else if (adjunctLesser(point.y(), lastXY.y(), lineBaseXY.y())) { //TODO: character has gone suprtscript - pass = STYLESUPERSCRIPT; + pass = FrameworkLineTests::STYLESUPERSCRIPT; qDebug() << "STYLESUPERSCRIPT point:" << point << " lastXY:" << lastXY << " lineBaseXY:" << lineBaseXY; } else if (adjunctGreater(point.y(),lastXY.y(),lineBaseXY.y())) @@ -4000,13 +4000,13 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::linearTest(QPointF point, bool xInLim if (coLinera(point.y(), lineBaseXY.y())) //PDF never deviates from the line when it comes to colenear { // were back on track - pass = STYLENORMALRETURN; + pass = FrameworkLineTests::STYLENORMALRETURN; qDebug() << "STYLENORMALRETURN"; } else { //TODO: this character has overflowed the height, or is still superscript just not so much - pass = STYLESUPERSCRIPT; //could be STYLEBELOWBASELINE + pass = FrameworkLineTests::STYLESUPERSCRIPT; //could be STYLEBELOWBASELINE qDebug() << "STYLESUBSCRIPT point:" << point << " lastXY:" << lastXY << " lineBaseXY:" << lineBaseXY; //qDebug() << "STYLESUBSCRIPT: "; } @@ -4020,7 +4020,7 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::linearTest(QPointF point, bool xInLim if ((textRegionLines.size() >= 2)) //TODO: Need to setup some parameters replating to width matching, they mainly relate to justfication && closeToX(textRegionLines[textRegionLines.size() - 2].baseOrigin.x() + textRegionLines[textRegionLines.size() - 2].width, maxWidth)) { //TODO: add a new line and update the deltas - pass = NEWLINE; + pass = FrameworkLineTests::NEWLINE; #ifdef DEBUG_TEXT_IMPORT qDebug() << "NEWLINE1 point:" << point << " _lastXY:" << lastXY << " origin: " << textRegioBasenOrigin << " this:" << this << " linespacing: " << lineSpacing; @@ -4028,7 +4028,7 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::linearTest(QPointF point, bool xInLim } // we only have the first line so far, so pass without much of a test. else if (textRegionLines.size() == 1) { - pass = NEWLINE; + pass = FrameworkLineTests::NEWLINE; #ifdef DEBUG_TEXT_IMPORT qDebug() << "NEWLINE2 point:" << point << " _lastXY:" << lastXY << " origin: " << textRegioBasenOrigin << " this:" << this << " linespacing: " << lineSpacing; #endif @@ -4046,7 +4046,7 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::linearTest(QPointF point, bool xInLim } // Just perform some basic checks to see if newPoint can reasonably be asscribed to the current textframe. -TextRegion::FRAMEWORKLINETESTS TextRegion::isRegionConcurrent(QPointF newPoint) +TextRegion::FrameworkLineTests TextRegion::isRegionConcurrent(QPointF newPoint) { if (glyphs.empty()) { @@ -4069,12 +4069,12 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::isRegionConcurrent(QPointF newPoint) { yInLimits = true; } - FRAMEWORKLINETESTS pass = linearTest(newPoint, xInLimits, yInLimits); + FrameworkLineTests pass = linearTest(newPoint, xInLimits, yInLimits); return pass; } -TextRegion::FRAMEWORKLINETESTS TextRegion::moveToPoint(QPointF newPoint) +TextRegion::FrameworkLineTests TextRegion::moveToPoint(QPointF newPoint) { // //qDebug() << "moveToPoint: " << newPoint; @@ -4102,17 +4102,17 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::moveToPoint(QPointF newPoint) yInLimits = true; } //qDebug() << "newPoint8: " << newPoint; - FRAMEWORKLINETESTS pass = linearTest(newPoint, xInLimits, yInLimits); + FrameworkLineTests pass = linearTest(newPoint, xInLimits, yInLimits); //TODO: need to check to see if we are creating a new paragraph or not. basically if the cursor is returned to x origin before it reached x width. this could be returned as part of a matrix by linearTest that specifies exactly how the test ws passed. maybew return an enum with either the mode that passed or a failure value - if (pass != FRAMEWORKLINETESTS::FAIL) + if (pass != FrameworkLineTests::FAIL) { //create new lines and line segments depending upon the moide of the movement. TextRegionLine *textRegionLine = nullptr; - if (pass == NEWLINE || pass == FIRSTPOINT) + if (pass == FrameworkLineTests::NEWLINE || pass == FrameworkLineTests::FIRSTPOINT) { //qDebug() << "Newline: "; - if (pass == FIRSTPOINT) { + if (pass == FrameworkLineTests::FIRSTPOINT) { if (textRegionLines.empty()) { textRegionLines.push_back(TextRegionLine()); } @@ -4123,7 +4123,7 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::moveToPoint(QPointF newPoint) } textRegionLine = &textRegionLines.back(); textRegionLine->baseOrigin = newPoint; - if (pass == NEWLINE) + if (pass == FrameworkLineTests::NEWLINE) { textRegionLine->maxHeight = abs(newPoint.y() - lastXY.y()); if (textRegionLines.size() == 2) @@ -4135,14 +4135,14 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::moveToPoint(QPointF newPoint) } textRegionLine = &textRegionLines.back(); - if ((pass == FIRSTPOINT && textRegionLine->segments.empty()) || pass == NEWLINE || pass != FIRSTPOINT && textRegionLine->segments[0].glyphIndex != textRegionLine->glyphIndex) + if ((pass == FrameworkLineTests::FIRSTPOINT && textRegionLine->segments.empty()) || pass == FrameworkLineTests::NEWLINE || pass != FrameworkLineTests::FIRSTPOINT && textRegionLine->segments[0].glyphIndex != textRegionLine->glyphIndex) { TextRegionLine newSegment = TextRegionLine(); textRegionLine->segments.push_back(newSegment); } TextRegionLine* segment = &textRegionLine->segments.back(); segment->baseOrigin = newPoint; - if (pass == STYLESUPERSCRIPT) + if (pass == FrameworkLineTests::STYLESUPERSCRIPT) { segment->maxHeight = abs(lineSpacing - (newPoint.y() - lastXY.y())); @@ -4152,7 +4152,7 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::moveToPoint(QPointF newPoint) segment->maxHeight = textRegionLines.back().maxHeight; } - if (pass != NEWLINE && pass != FIRSTPOINT) + if (pass != FrameworkLineTests::NEWLINE && pass != FrameworkLineTests::FIRSTPOINT) { textRegionLines.back().segments.back().width = abs(textRegionLines.back().segments.back().baseOrigin.x() - newPoint.x()); textRegionLine = &textRegionLines.back(); @@ -4171,7 +4171,7 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::moveToPoint(QPointF newPoint) } //TODO:, extract some font heights instesad of using dx all the time -TextRegion::FRAMEWORKLINETESTS TextRegion::addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph newGlyph) +TextRegion::FrameworkLineTests TextRegion::addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph newGlyph) { QPointF movedGlyphPoint = QPointF(newGlyphPoint.x() + newGlyph.dx, newGlyphPoint.y() + newGlyph.dy); //qDebug() << "addGlyphAtPoint start" << newGlyphPoint << " glyph:"<< new_glyph.code; @@ -4194,14 +4194,14 @@ TextRegion::FRAMEWORKLINETESTS TextRegion::addGlyphAtPoint(QPointF newGlyphPoint { yInLimits = true; } - FRAMEWORKLINETESTS pass = linearTest(newGlyphPoint, xInLimits, yInLimits); - if (pass != FRAMEWORKLINETESTS::FAIL) + FrameworkLineTests pass = linearTest(newGlyphPoint, xInLimits, yInLimits); + if (pass != FrameworkLineTests::FAIL) { maxHeight = abs(textRegioBasenOrigin.y() - movedGlyphPoint.y()) + lineSpacing > maxHeight ? abs(textRegioBasenOrigin.y() - movedGlyphPoint.y()) + lineSpacing : maxHeight; //moveto deals with setting newlines and segments, all we hve to do is populatit them with the parameters tyhe glyph gives us such as it's width and height and set the glyph index for the newlines and segments TextRegionLine* textRegionLine = &textRegionLines.back(); - if (pass == NEWLINE || pass == FIRSTPOINT) { + if (pass == FrameworkLineTests::NEWLINE || pass == FrameworkLineTests::FIRSTPOINT) { textRegionLine->glyphIndex = glyphs.size() - 1; textRegionLine->baseOrigin = QPointF(textRegioBasenOrigin.x(), newGlyphPoint.y()); } @@ -4329,7 +4329,7 @@ void SlaOutputDev::updateTextPos(GfxState* state) ) { activeTextRegion->textRegioBasenOrigin = newPosition; - m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::ADDFIRSTCHAR]; + m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::AddCharMode::ADDFIRSTCHAR]; } else { @@ -4338,7 +4338,7 @@ void SlaOutputDev::updateTextPos(GfxState* state) { QPointF glyphPosition = activeTextRegion->lastXY; activeTextRegion->lastXY.setX(activeTextRegion->lastXY.x() - activeTextRegion->glyphs.back().dx); - if (activeTextRegion->addGlyphAtPoint(glyphPosition, activeTextRegion->glyphs.back()) == TextRegion::FAIL) { + if (activeTextRegion->addGlyphAtPoint(glyphPosition, activeTextRegion->glyphs.back()) == TextRegion::FrameworkLineTests::FAIL) { qDebug("FIXME: Rogue glyph detected, this should never happen because the cursor should move before glyphs in new regions are added."); } else @@ -4347,8 +4347,8 @@ void SlaOutputDev::updateTextPos(GfxState* state) }; } } - TextRegion::FRAMEWORKLINETESTS lineTestResult = activeTextRegion->moveToPoint(newPosition); - if (lineTestResult == TextRegion::FAIL) + TextRegion::FrameworkLineTests lineTestResult = activeTextRegion->moveToPoint(newPosition); + if (lineTestResult == TextRegion::FrameworkLineTests::FAIL) { // FIXME: render the textframe, this should be done after the document has finished loading the current page so all the layout fixups can be put inplace first renderTextFrame(); @@ -4513,7 +4513,7 @@ void AddFirstChar::addChar(GfxState* state, double x, double y, double dx, doubl newGlyph.dx = dx; newGlyph.dy = dy; - m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::ADDBASICCHAR]; + m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::AddCharMode::ADDBASICCHAR]; // Convert the character to UTF-16 since that's our SVG document's encoding for (int i = 0; i < uLen; i++) @@ -4526,7 +4526,7 @@ void AddFirstChar::addChar(GfxState* state, double x, double y, double dx, doubl m_textFramework->activeTextRegion.glyphs.push_back(newGlyph); //only need to be called for the very first point - if (m_textFramework->activeTextRegion.addGlyphAtPoint(QPointF(x, y), newGlyph) == TextRegion::FAIL) + if (m_textFramework->activeTextRegion.addGlyphAtPoint(QPointF(x, y), newGlyph) == TextRegion::FrameworkLineTests::FAIL) { qDebug("FIXME: Rogue glyph detected, this should never happen because the coursor should move before glyphs in new regions are added."); } @@ -4562,26 +4562,26 @@ TextFramework::TextFramework() { m_textRegions.push_back(activeTextRegion); //have a map of reusable addchar implementations instead of creating and deleting them all the time. - addCharModes[ADDCHARMODE::ADDFIRSTCHAR] = new AddFirstChar(this); - addCharModes[ADDCHARMODE::ADDBASICCHAR] = new AddBasicChar(this); - addCharModes[ADDCHARMODE::ADDCHARWITHNEWSTYLE] = new AddCharWithNewStyle(this); - addCharModes[ADDCHARMODE::ADDCHARWITHPREVIOUSSTYLE] = new AddCharWithPreviousStyle(this); + addCharModes[AddCharMode::ADDFIRSTCHAR] = new AddFirstChar(this); + addCharModes[AddCharMode::ADDBASICCHAR] = new AddBasicChar(this); + addCharModes[AddCharMode::ADDCHARWITHNEWSTYLE] = new AddCharWithNewStyle(this); + addCharModes[AddCharMode::ADDCHARWITHPREVIOUSSTYLE] = new AddCharWithPreviousStyle(this); } TextFramework::~TextFramework() { //FIXME: could probably enumberate this - delete addCharModes[ADDCHARMODE::ADDFIRSTCHAR]; - delete addCharModes[ADDCHARMODE::ADDBASICCHAR]; - delete addCharModes[ADDCHARMODE::ADDCHARWITHNEWSTYLE]; - delete addCharModes[ADDCHARMODE::ADDCHARWITHPREVIOUSSTYLE]; + delete addCharModes[AddCharMode::ADDFIRSTCHAR]; + delete addCharModes[AddCharMode::ADDBASICCHAR]; + delete addCharModes[AddCharMode::ADDCHARWITHNEWSTYLE]; + delete addCharModes[AddCharMode::ADDCHARWITHPREVIOUSSTYLE]; } void TextFramework::addNewTextRegion() { activeTextRegion = TextRegion(); m_textRegions.push_back(activeTextRegion); - addChar = addCharModes[TextFramework::ADDFIRSTCHAR]; + addChar = addCharModes[TextFramework::AddCharMode::ADDFIRSTCHAR]; } bool TextFramework::isNewLineOrRegion(QPointF newPosition) diff --git a/scribus/plugins/import/pdf/slaoutput.h b/scribus/plugins/import/pdf/slaoutput.h index 948695375..33e542182 100644 --- a/scribus/plugins/import/pdf/slaoutput.h +++ b/scribus/plugins/import/pdf/slaoutput.h @@ -180,7 +180,7 @@ class TextRegionLine class TextRegion { public: - enum FRAMEWORKLINETESTS { + enum class FrameworkLineTests { FIRSTPOINT, SAMELINE, STYLESUPERSCRIPT, @@ -203,10 +203,10 @@ class TextRegion { bool closeToY(qreal y1, qreal y2); bool adjunctLesser(qreal testY, qreal lastY, qreal baseY); bool adjunctGreater(qreal testY, qreal lastY, qreal baseY); - TextRegion::FRAMEWORKLINETESTS linearTest(QPointF point, bool xInLimits, bool yInLimits); - TextRegion::FRAMEWORKLINETESTS isRegionConcurrent(QPointF newPoint); - TextRegion::FRAMEWORKLINETESTS moveToPoint(QPointF newPoint); - TextRegion::FRAMEWORKLINETESTS addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph new_glyph); + TextRegion::FrameworkLineTests linearTest(QPointF point, bool xInLimits, bool yInLimits); + TextRegion::FrameworkLineTests isRegionConcurrent(QPointF newPoint); + TextRegion::FrameworkLineTests moveToPoint(QPointF newPoint); + TextRegion::FrameworkLineTests addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph new_glyph); void renderToTextFrame(PageItem* textNode, ParagraphStyle& pStyle); std::vector glyphs; bool isNew(); @@ -224,13 +224,13 @@ class TextFramework public: TextFramework(); ~TextFramework(); - enum ADDCHARMODE { + enum class AddCharMode { ADDFIRSTCHAR, ADDBASICCHAR, ADDCHARWITHNEWSTYLE, ADDCHARWITHPREVIOUSSTYLE }; - std::map addCharModes; + std::map addCharModes; TextRegion& activeTextRegion = TextRegion(); //faster than calling back on the vector all the time. void addNewTextRegion(); AddCharInterface* addChar = nullptr; From bdac89d90451832dac8d04d56bb75f84845ec4de Mon Sep 17 00:00:00 2001 From: olivetthered <37796246+olivetthered@users.noreply.github.com> Date: Tue, 9 Jun 2020 11:31:01 +0100 Subject: [PATCH 04/13] fix { positions --- scribus/plugins/import/pdf/slaoutput.h | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/scribus/plugins/import/pdf/slaoutput.h b/scribus/plugins/import/pdf/slaoutput.h index 33e542182..0e4bd114e 100644 --- a/scribus/plugins/import/pdf/slaoutput.h +++ b/scribus/plugins/import/pdf/slaoutput.h @@ -158,7 +158,8 @@ class AnoOutputDev : public OutputDev * Holds all the dtails for each glyph in the text imported from the pdf file. * */ -struct PdfGlyph { +struct PdfGlyph +{ double dx; // X advance value double dy; // Y advance value double rise; // Text rise parameter @@ -170,7 +171,7 @@ class TextRegionLine { public: qreal maxHeight = 0; - //we can probably use maxHeight for this. + //we can probably use maxHeight for this. qreal width = 0; int glyphIndex = 0; QPointF baseOrigin = QPointF(0, 0); @@ -178,9 +179,11 @@ class TextRegionLine }; -class TextRegion { +class TextRegion +{ public: - enum class FrameworkLineTests { + enum class FrameworkLineTests + { FIRSTPOINT, SAMELINE, STYLESUPERSCRIPT, @@ -207,7 +210,7 @@ class TextRegion { TextRegion::FrameworkLineTests isRegionConcurrent(QPointF newPoint); TextRegion::FrameworkLineTests moveToPoint(QPointF newPoint); TextRegion::FrameworkLineTests addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph new_glyph); - void renderToTextFrame(PageItem* textNode, ParagraphStyle& pStyle); + void renderToTextFrame(PageItem* textNode, ParagraphStyle& pStyle); std::vector glyphs; bool isNew(); }; @@ -215,7 +218,7 @@ class TextRegion { class AddCharInterface { public: - // Pure Virtual Function + // Pure Virtual Function virtual void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) = 0; }; @@ -224,7 +227,8 @@ class TextFramework public: TextFramework(); ~TextFramework(); - enum class AddCharMode { + enum class AddCharMode + { ADDFIRSTCHAR, ADDBASICCHAR, ADDCHARWITHNEWSTYLE, @@ -317,7 +321,7 @@ class SlaOutputDev : public OutputDev void endMarkedContent(GfxState *state) override; void markPoint(POPPLER_CONST char *name) override; void markPoint(POPPLER_CONST char *name, Dict *properties) override; - + //----- image drawing void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool interpolate, GBool inlineImg) override; @@ -393,7 +397,7 @@ class SlaOutputDev : public OutputDev void createImageFrame(QImage& image, GfxState *state, int numColorComponents); - //PDF Textbox framework + //PDF Textbox framework void setFillAndStrokeForPDF(GfxState* state, PageItem* text_node); void updateTextPos(GfxState* state) override; void renderTextFrame(); From e5f390cb890a3aa2a847ad744c1ef6397d192c43 Mon Sep 17 00:00:00 2001 From: olivetthered <37796246+olivetthered@users.noreply.github.com> Date: Thu, 11 Jun 2020 14:57:37 +0100 Subject: [PATCH 05/13] replace addChar classes with textFrame function pointers replace addChar classes with dynamically mapped textFrame function pointers fix a couple of potential overflows that were picked upo as compiler wqarnings fix { should be on a new line for new and some existing code --- scribus/plugins/import/pdf/slaoutput.cpp | 144 +++++++++++------------ scribus/plugins/import/pdf/slaoutput.h | 110 ++++++++--------- 2 files changed, 113 insertions(+), 141 deletions(-) diff --git a/scribus/plugins/import/pdf/slaoutput.cpp b/scribus/plugins/import/pdf/slaoutput.cpp index 230bdc5a5..5988ba76f 100644 --- a/scribus/plugins/import/pdf/slaoutput.cpp +++ b/scribus/plugins/import/pdf/slaoutput.cpp @@ -286,8 +286,7 @@ SlaOutputDev::SlaOutputDev(ScribusDoc* doc, QList *Elements, QStringL layersSetByOCG = false; importTextAsVectors = true; - m_textFramework = new TextFramework(); - m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::AddCharMode::ADDFIRSTCHAR]; + m_textFramework = new TextFramework(); } SlaOutputDev::~SlaOutputDev() @@ -2515,11 +2514,11 @@ void SlaOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str ImageStream * imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); unsigned int *dest = nullptr; - unsigned char * buffer = new unsigned char[width * height * 4]; + unsigned char * buffer = new unsigned char[(long)width * (long)height * 4]; QImage * image = nullptr; for (int y = 0; y < height; y++) { - dest = (unsigned int *)(buffer + y * 4 * width); + dest = (unsigned int *)(buffer + y * 4 * (long)width); Guchar * pix = imgStr->getLine(); colorMap->getRGBLine(pix, dest, width); } @@ -2534,11 +2533,11 @@ void SlaOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str ImageStream *mskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); mskStr->reset(); Guchar *mdest = nullptr; - unsigned char * mbuffer = new unsigned char[maskWidth * maskHeight]; - memset(mbuffer, 0, maskWidth * maskHeight); + unsigned char * mbuffer = new unsigned char[(long)maskWidth * (long)maskHeight]; + memset(mbuffer, 0, (long)maskWidth * (long)maskHeight); for (int y = 0; y < maskHeight; y++) { - mdest = (Guchar *)(mbuffer + y * maskWidth); + mdest = (Guchar *)(mbuffer + y * (long)maskWidth); Guchar * pix = mskStr->getLine(); maskColorMap->getGrayLine(pix, mdest, maskWidth); } @@ -2595,11 +2594,11 @@ void SlaOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, i ImageStream * imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); unsigned int *dest = nullptr; - unsigned char * buffer = new unsigned char[width * height * 4]; + unsigned char * buffer = new unsigned char[(long)width * (long)height * 4]; QImage * image = nullptr; for (int y = 0; y < height; y++) { - dest = (unsigned int *)(buffer + y * 4 * width); + dest = (unsigned int *)(buffer + y * 4 * (long)width); Guchar * pix = imgStr->getLine(); colorMap->getRGBLine(pix, dest, width); } @@ -2615,11 +2614,11 @@ void SlaOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, i mskStr->reset(); Guchar *mdest = nullptr; int invert_bit = maskInvert ? 1 : 0; - unsigned char * mbuffer = new unsigned char[maskWidth * maskHeight]; - memset(mbuffer, 0, maskWidth * maskHeight); + unsigned char * mbuffer = new unsigned char[(long)maskWidth * (long)maskHeight]; + memset(mbuffer, 0, (long)maskWidth * (long)maskHeight); for (int y = 0; y < maskHeight; y++) { - mdest = (Guchar *)(mbuffer + y * maskWidth); + mdest = (Guchar *)(mbuffer + y * (long)maskWidth); Guchar * pix = mskStr->getLine(); for (int x = 0; x < maskWidth; x++) { @@ -2726,7 +2725,8 @@ void SlaOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int widt } } - if (image != nullptr && !image->isNull()) { + if (image != nullptr && !image->isNull()) + { createImageFrame(*image, state, colorMap->getNumPixelComps()); } @@ -3048,11 +3048,13 @@ void SlaOutputDev::updateFontForVector(GfxState *state) tmpBuf = nullptr; fontLoc = nullptr; - if (!(gfxFont = state->getFont())) { + if (!(gfxFont = state->getFont())) + { goto err1; } fontType = gfxFont->getType(); - if (fontType == fontType3) { + if (fontType == fontType3) + { goto err1; } @@ -3092,7 +3094,8 @@ void SlaOutputDev::updateFontForVector(GfxState *state) fontsrc->setBuf(tmpBuf, tmpBufLen, gTrue); // load the font file - switch (fontType) { + switch (fontType) + { case fontType1: if (!(fontFile = m_fontEngine->loadType1Font( id, @@ -3362,7 +3365,7 @@ void SlaOutputDev::drawChar(GfxState* state, double x, double y, double dx, doub } } if (!importTextAsVectors) { // donm't render the char as vectors add it to an array so it can be rendred as a string - m_textFramework->addChar->addChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + (m_textFramework ->*(m_textFramework->addChar))(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); } } } @@ -3455,7 +3458,7 @@ void SlaOutputDev::endTextObject(GfxState *state) qDebug("FIXME:Rogue textblock"); } - m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::AddCharMode::ADDFIRSTCHAR]; + m_textFramework->setCharMode(TextFramework::AddCharMode::ADDFIRSTCHAR); // qDebug() << "SlaOutputDev::endTextObject"; if (!m_clipTextPath.isEmpty()) { @@ -4015,7 +4018,8 @@ TextRegion::FrameworkLineTests TextRegion::linearTest(QPointF point, bool xInLim //TODO: We need to calculate things like new parargraphs and left hand justification if (closeToX(point.x(), textRegioBasenOrigin.x())) { - if (closeToY(point.y(), lastXY.y()) && !coLinera(point.y(), lastXY.y())) { + if (closeToY(point.y(), lastXY.y()) && !coLinera(point.y(), lastXY.y())) + { //TODO: We need to calculate things like new parargraphs and left hand justification if ((textRegionLines.size() >= 2)) //TODO: Need to setup some parameters replating to width matching, they mainly relate to justfication && closeToX(textRegionLines[textRegionLines.size() - 2].baseOrigin.x() + textRegionLines[textRegionLines.size() - 2].width, maxWidth)) { @@ -4112,7 +4116,8 @@ TextRegion::FrameworkLineTests TextRegion::moveToPoint(QPointF newPoint) if (pass == FrameworkLineTests::NEWLINE || pass == FrameworkLineTests::FIRSTPOINT) { //qDebug() << "Newline: "; - if (pass == FrameworkLineTests::FIRSTPOINT) { + if (pass == FrameworkLineTests::FIRSTPOINT) + { if (textRegionLines.empty()) { textRegionLines.push_back(TextRegionLine()); } @@ -4329,7 +4334,7 @@ void SlaOutputDev::updateTextPos(GfxState* state) ) { activeTextRegion->textRegioBasenOrigin = newPosition; - m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::AddCharMode::ADDFIRSTCHAR]; + m_textFramework->setCharMode(TextFramework::AddCharMode::ADDFIRSTCHAR); } else { @@ -4505,36 +4510,48 @@ void SlaOutputDev::finishItem(PageItem* item) //item->setLineTransparency(1.0); } +TextFramework::TextFramework() +{ + m_textRegions.push_back(activeTextRegion); + setCharMode(AddCharMode::ADDFIRSTCHAR); +} -void AddFirstChar::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +TextFramework::~TextFramework() { - //qDebug() << "addChar() '" << u << " : " << uLen; - PdfGlyph newGlyph; - newGlyph.dx = dx; - newGlyph.dy = dy; +} - m_textFramework->addChar = m_textFramework->addCharModes[TextFramework::AddCharMode::ADDBASICCHAR]; +void TextFramework::addNewTextRegion() +{ + activeTextRegion = TextRegion(); + m_textRegions.push_back(activeTextRegion); + setCharMode(TextFramework::AddCharMode::ADDFIRSTCHAR); +} - // Convert the character to UTF-16 since that's our SVG document's encoding - for (int i = 0; i < uLen; i++) - { - newGlyph.code = (char16_t)u[i]; - } - - newGlyph.rise = state->getRise(); - //m_slaOutputDev->activeTextRegion.lastXY = QPointF(x, y); - m_textFramework->activeTextRegion.glyphs.push_back(newGlyph); +bool TextFramework::isNewLineOrRegion(QPointF newPosition) +{ + return (activeTextRegion.coLinera(activeTextRegion.lastXY.y(), activeTextRegion.textRegionLines.back().baseOrigin.y()) && + !activeTextRegion.coLinera(newPosition.y(), activeTextRegion.lastXY.y())) + || (activeTextRegion.coLinera(newPosition.y(), activeTextRegion.lastXY.y()) + && !activeTextRegion.closeToX(newPosition.x(), activeTextRegion.lastXY.x())); +} + +PdfGlyph TextFramework::AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +{ + //qDebug() << "AddFirstChar() '" << u << " : " << uLen; + auto newGlyph = AddBasicChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + + setCharMode(AddCharMode::ADDBASICCHAR); //only need to be called for the very first point - if (m_textFramework->activeTextRegion.addGlyphAtPoint(QPointF(x, y), newGlyph) == TextRegion::FrameworkLineTests::FAIL) + if (activeTextRegion.addGlyphAtPoint(QPointF(x, y), newGlyph) == TextRegion::FrameworkLineTests::FAIL) { qDebug("FIXME: Rogue glyph detected, this should never happen because the coursor should move before glyphs in new regions are added."); } } -void AddBasicChar::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +PdfGlyph TextFramework::AddBasicChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { - //qDebug() << "addChar() '" << u << " : " << uLen; + //qDebug() << "AddBasicChar() '" << u << " : " << uLen; PdfGlyph newGlyph; newGlyph.dx = dx; newGlyph.dy = dy; @@ -4546,48 +4563,21 @@ void AddBasicChar::addChar(GfxState* state, double x, double y, double dx, doubl } newGlyph.rise = state->getRise(); - m_textFramework->activeTextRegion.lastXY = QPointF(x, y); - m_textFramework->activeTextRegion.glyphs.push_back(newGlyph); -} - -void AddCharWithPreviousStyle::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) -{ -} - -void AddCharWithNewStyle::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) -{ -} - -TextFramework::TextFramework() -{ - m_textRegions.push_back(activeTextRegion); - //have a map of reusable addchar implementations instead of creating and deleting them all the time. - addCharModes[AddCharMode::ADDFIRSTCHAR] = new AddFirstChar(this); - addCharModes[AddCharMode::ADDBASICCHAR] = new AddBasicChar(this); - addCharModes[AddCharMode::ADDCHARWITHNEWSTYLE] = new AddCharWithNewStyle(this); - addCharModes[AddCharMode::ADDCHARWITHPREVIOUSSTYLE] = new AddCharWithPreviousStyle(this); + activeTextRegion.lastXY = QPointF(x, y); + activeTextRegion.glyphs.push_back(newGlyph); + return newGlyph; } -TextFramework::~TextFramework() +PdfGlyph TextFramework::AddCharWithNewStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { - //FIXME: could probably enumberate this - delete addCharModes[AddCharMode::ADDFIRSTCHAR]; - delete addCharModes[AddCharMode::ADDBASICCHAR]; - delete addCharModes[AddCharMode::ADDCHARWITHNEWSTYLE]; - delete addCharModes[AddCharMode::ADDCHARWITHPREVIOUSSTYLE]; + //qDebug() << "AddCharWithNewStyle() '" << u << " : " << uLen; + auto newGlyph = AddBasicChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + return newGlyph; } -void TextFramework::addNewTextRegion() +PdfGlyph TextFramework::AddCharWithPreviousStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { - activeTextRegion = TextRegion(); - m_textRegions.push_back(activeTextRegion); - addChar = addCharModes[TextFramework::AddCharMode::ADDFIRSTCHAR]; -} - -bool TextFramework::isNewLineOrRegion(QPointF newPosition) -{ - return (activeTextRegion.coLinera(activeTextRegion.lastXY.y(), activeTextRegion.textRegionLines.back().baseOrigin.y()) && - !activeTextRegion.coLinera(newPosition.y(), activeTextRegion.lastXY.y())) - || (activeTextRegion.coLinera(newPosition.y(), activeTextRegion.lastXY.y()) - && !activeTextRegion.closeToX(newPosition.x(), activeTextRegion.lastXY.x())); + //qDebug() << "AddCharWithPreviousStyle() '" << u << " : " << uLen; + auto newGlyph = AddBasicChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + return newGlyph; } diff --git a/scribus/plugins/import/pdf/slaoutput.h b/scribus/plugins/import/pdf/slaoutput.h index 0e4bd114e..1751e2447 100644 --- a/scribus/plugins/import/pdf/slaoutput.h +++ b/scribus/plugins/import/pdf/slaoutput.h @@ -158,7 +158,7 @@ class AnoOutputDev : public OutputDev * Holds all the dtails for each glyph in the text imported from the pdf file. * */ -struct PdfGlyph +struct PdfGlyph { double dx; // X advance value double dy; // Y advance value @@ -171,7 +171,7 @@ class TextRegionLine { public: qreal maxHeight = 0; - //we can probably use maxHeight for this. + //we can probably use maxHeight for this. qreal width = 0; int glyphIndex = 0; QPointF baseOrigin = QPointF(0, 0); @@ -182,7 +182,7 @@ class TextRegionLine class TextRegion { public: - enum class FrameworkLineTests + enum class FrameworkLineTests { FIRSTPOINT, SAMELINE, @@ -210,23 +210,18 @@ class TextRegion TextRegion::FrameworkLineTests isRegionConcurrent(QPointF newPoint); TextRegion::FrameworkLineTests moveToPoint(QPointF newPoint); TextRegion::FrameworkLineTests addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph new_glyph); - void renderToTextFrame(PageItem* textNode, ParagraphStyle& pStyle); + void renderToTextFrame(PageItem* textNode, ParagraphStyle& pStyle); std::vector glyphs; bool isNew(); }; -class AddCharInterface -{ -public: - // Pure Virtual Function - virtual void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) = 0; -}; class TextFramework { public: TextFramework(); ~TextFramework(); + enum class AddCharMode { ADDFIRSTCHAR, @@ -234,13 +229,50 @@ class TextFramework ADDCHARWITHNEWSTYLE, ADDCHARWITHPREVIOUSSTYLE }; - std::map addCharModes; + + void setCharMode(AddCharMode mode) + { + auto addCharModal = addCharModes[mode]; + if (!addCharModal) + { + addCharModal = nullptr; + switch (mode) + { + case AddCharMode::ADDFIRSTCHAR: + addCharModal = AddFirstChar;; + break; + case AddCharMode::ADDBASICCHAR: + addCharModal = AddBasicChar; + break; + case AddCharMode::ADDCHARWITHNEWSTYLE: + addCharModal = AddCharWithNewStyle; + break; + case AddCharMode::ADDCHARWITHPREVIOUSSTYLE: + addCharModal = AddCharWithPreviousStyle; + break; + default: + // FIXME: This should never happen, set to a default + addCharModal = AddBasicChar; + } + addCharModes[mode] = addCharModal; + } + addChar = addCharModal; + } + + //AddCharInterface* createChar(AddCharMode::ADDFIRSTCHAR); + //AddCharInterface* createChar(AddCharMode::ADDBASICCHAR) + + std::map addCharModes; TextRegion& activeTextRegion = TextRegion(); //faster than calling back on the vector all the time. void addNewTextRegion(); - AddCharInterface* addChar = nullptr; + PdfGlyph(TextFramework::* addChar)(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) = {}; bool isNewLineOrRegion(QPointF newPosition); private: std::vector m_textRegions = std::vector(); + PdfGlyph AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); + PdfGlyph AddBasicChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); + PdfGlyph AddCharWithNewStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); + PdfGlyph AddCharWithPreviousStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); }; class SlaOutputDev : public OutputDev @@ -321,7 +353,7 @@ class SlaOutputDev : public OutputDev void endMarkedContent(GfxState *state) override; void markPoint(POPPLER_CONST char *name) override; void markPoint(POPPLER_CONST char *name, Dict *properties) override; - + //----- image drawing void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool interpolate, GBool inlineImg) override; @@ -397,7 +429,7 @@ class SlaOutputDev : public OutputDev void createImageFrame(QImage& image, GfxState *state, int numColorComponents); - //PDF Textbox framework + //PDF Textbox framework void setFillAndStrokeForPDF(GfxState* state, PageItem* text_node); void updateTextPos(GfxState* state) override; void renderTextFrame(); @@ -473,54 +505,4 @@ class SlaOutputDev : public OutputDev TextFramework* m_textFramework = nullptr; }; -class AddFirstChar : public AddCharInterface -{ -public: - AddFirstChar(TextFramework* textFramework) - { - m_textFramework = textFramework; - } - void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) override; -private: - TextFramework* m_textFramework = nullptr; -}; - -class AddBasicChar : public AddCharInterface -{ -public: - AddBasicChar(TextFramework* textFramework) - { - m_textFramework = textFramework; - } - void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) override; -private: - TextFramework* m_textFramework = nullptr; -}; - -// TODO: implement these addchar definitions so that they can handle changes in style, font, text micro positioning, scaling, matrix etc... -class AddCharWithNewStyle : public AddCharInterface -{ -public: - AddCharWithNewStyle(TextFramework* textFramework) - { - m_textFramework = textFramework; - } - void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) override; -private: - TextFramework* m_textFramework = nullptr; -}; - -// TODO: implement these addchar definitions so that they can handle changes in style, font, text micro positioning, scaling, matrix etc... -class AddCharWithPreviousStyle : public AddCharInterface -{ -public: - AddCharWithPreviousStyle(TextFramework* textFramework) - { - m_textFramework = textFramework; - } - void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) override; -private: - TextFramework* m_textFramework = nullptr; -}; - #endif From 824594d3d5a793cb4555ae44778d59ceb6460db7 Mon Sep 17 00:00:00 2001 From: olivetthered <37796246+olivetthered@users.noreply.github.com> Date: Thu, 11 Jun 2020 15:08:23 +0100 Subject: [PATCH 06/13] fix regression, don't set lastxy if were adding the first char fix regression, don't set lastxy if were adding the first char --- scribus/plugins/import/pdf/slaoutput.cpp | 18 ++++++++++++------ scribus/plugins/import/pdf/slaoutput.h | 1 + 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/scribus/plugins/import/pdf/slaoutput.cpp b/scribus/plugins/import/pdf/slaoutput.cpp index 5988ba76f..0f9765498 100644 --- a/scribus/plugins/import/pdf/slaoutput.cpp +++ b/scribus/plugins/import/pdf/slaoutput.cpp @@ -4538,8 +4538,7 @@ bool TextFramework::isNewLineOrRegion(QPointF newPosition) PdfGlyph TextFramework::AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { //qDebug() << "AddFirstChar() '" << u << " : " << uLen; - auto newGlyph = AddBasicChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); - + auto newGlyph = TextFramework::AddCharCommon(state, x, y, dx, dy, u, uLen); setCharMode(AddCharMode::ADDBASICCHAR); //only need to be called for the very first point @@ -4548,8 +4547,7 @@ PdfGlyph TextFramework::AddFirstChar(GfxState* state, double x, double y, double qDebug("FIXME: Rogue glyph detected, this should never happen because the coursor should move before glyphs in new regions are added."); } } - -PdfGlyph TextFramework::AddBasicChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +PdfGlyph TextFramework::AddCharCommon(GfxState* state, double x, double y, double dx, double dy, Unicode const* u, int uLen) { //qDebug() << "AddBasicChar() '" << u << " : " << uLen; PdfGlyph newGlyph; @@ -4563,6 +4561,12 @@ PdfGlyph TextFramework::AddBasicChar(GfxState* state, double x, double y, double } newGlyph.rise = state->getRise(); + return newGlyph; +} + +PdfGlyph TextFramework::AddBasicChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +{ + auto newGlyph = AddCharCommon(state, x, y, dx, dy, u, uLen); activeTextRegion.lastXY = QPointF(x, y); activeTextRegion.glyphs.push_back(newGlyph); return newGlyph; @@ -4571,13 +4575,15 @@ PdfGlyph TextFramework::AddBasicChar(GfxState* state, double x, double y, double PdfGlyph TextFramework::AddCharWithNewStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { //qDebug() << "AddCharWithNewStyle() '" << u << " : " << uLen; - auto newGlyph = AddBasicChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + auto newGlyph = AddCharCommon(state, x, y, dx, dy, u, uLen); + activeTextRegion.glyphs.push_back(newGlyph); return newGlyph; } PdfGlyph TextFramework::AddCharWithPreviousStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { //qDebug() << "AddCharWithPreviousStyle() '" << u << " : " << uLen; - auto newGlyph = AddBasicChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + auto newGlyph = AddCharCommon(state, x, y, dx, dy, u, uLen); + activeTextRegion.glyphs.push_back(newGlyph); return newGlyph; } diff --git a/scribus/plugins/import/pdf/slaoutput.h b/scribus/plugins/import/pdf/slaoutput.h index 1751e2447..4a5a20bcb 100644 --- a/scribus/plugins/import/pdf/slaoutput.h +++ b/scribus/plugins/import/pdf/slaoutput.h @@ -269,6 +269,7 @@ class TextFramework bool isNewLineOrRegion(QPointF newPosition); private: std::vector m_textRegions = std::vector(); + PdfGlyph AddCharCommon(GfxState* state, double x, double y, double dx, double dy, Unicode const* u, int uLen); PdfGlyph AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); PdfGlyph AddBasicChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); PdfGlyph AddCharWithNewStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); From 4a0752b5b449eef641fff28d4a82f0a80679b896 Mon Sep 17 00:00:00 2001 From: olivetthered <37796246+olivetthered@users.noreply.github.com> Date: Thu, 11 Jun 2020 15:28:52 +0100 Subject: [PATCH 07/13] makje addchar private and implement a public caller makje addchar private and implement a public caller not that we have setCharMode --- scribus/plugins/import/pdf/slaoutput.cpp | 7 +++++- scribus/plugins/import/pdf/slaoutput.h | 28 ++++++++++++------------ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/scribus/plugins/import/pdf/slaoutput.cpp b/scribus/plugins/import/pdf/slaoutput.cpp index 0f9765498..db9be34e1 100644 --- a/scribus/plugins/import/pdf/slaoutput.cpp +++ b/scribus/plugins/import/pdf/slaoutput.cpp @@ -3365,7 +3365,7 @@ void SlaOutputDev::drawChar(GfxState* state, double x, double y, double dx, doub } } if (!importTextAsVectors) { // donm't render the char as vectors add it to an array so it can be rendred as a string - (m_textFramework ->*(m_textFramework->addChar))(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + m_textFramework->addChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); } } } @@ -4527,6 +4527,11 @@ void TextFramework::addNewTextRegion() setCharMode(TextFramework::AddCharMode::ADDFIRSTCHAR); } +void TextFramework::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +{ + (this->*(addChar))(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); +} + bool TextFramework::isNewLineOrRegion(QPointF newPosition) { return (activeTextRegion.coLinera(activeTextRegion.lastXY.y(), activeTextRegion.textRegionLines.back().baseOrigin.y()) && diff --git a/scribus/plugins/import/pdf/slaoutput.h b/scribus/plugins/import/pdf/slaoutput.h index 4a5a20bcb..ac97ad48c 100644 --- a/scribus/plugins/import/pdf/slaoutput.h +++ b/scribus/plugins/import/pdf/slaoutput.h @@ -170,11 +170,11 @@ struct PdfGlyph class TextRegionLine { public: - qreal maxHeight = 0; + qreal maxHeight = ; //we can probably use maxHeight for this. - qreal width = 0; - int glyphIndex = 0; - QPointF baseOrigin = QPointF(0, 0); + qreal width = {}; + int glyphIndex = {}; + QPointF baseOrigin = QPointF({}, {}); std::vector segments = std::vector(); }; @@ -194,13 +194,13 @@ class TextRegion FAIL }; - QPointF textRegioBasenOrigin = QPointF(0, 0); - qreal maxHeight = 0; - qreal lineSpacing = 1; + QPointF textRegioBasenOrigin = QPointF({}, {}); + qreal maxHeight = {}; + qreal lineSpacing = { 1 }; std::vector textRegionLines = std::vector(); - qreal maxWidth = 0; - QPointF lineBaseXY = QPointF(0, 0); //updated with the best match left value from all the textRegionLines and the best bottom value from the textRegionLines.segments; - QPointF lastXY = QPointF(0, 0); + qreal maxWidth = {}; + QPointF lineBaseXY = QPointF({ }, { }); //updated with the best match left value from all the textRegionLines and the best bottom value from the textRegionLines.segments; + QPointF lastXY = QPointF({}, {}); static bool coLinera(qreal a, qreal b); bool closeToX(qreal x1, qreal x2); bool closeToY(qreal y1, qreal y2); @@ -256,18 +256,18 @@ class TextFramework } addCharModes[mode] = addCharModal; } - addChar = addCharModal; + m_addChar = addCharModal; } - //AddCharInterface* createChar(AddCharMode::ADDFIRSTCHAR); - //AddCharInterface* createChar(AddCharMode::ADDBASICCHAR) + std::map addCharModes; TextRegion& activeTextRegion = TextRegion(); //faster than calling back on the vector all the time. void addNewTextRegion(); - PdfGlyph(TextFramework::* addChar)(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) = {}; + void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); bool isNewLineOrRegion(QPointF newPosition); private: + PdfGlyph(TextFramework::* m_addChar)(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) = {}; std::vector m_textRegions = std::vector(); PdfGlyph AddCharCommon(GfxState* state, double x, double y, double dx, double dy, Unicode const* u, int uLen); PdfGlyph AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); From dd32abf1bd8d44ff7eaf072d6d33b1a1bb301dc7 Mon Sep 17 00:00:00 2001 From: olivetthered <37796246+olivetthered@users.noreply.github.com> Date: Thu, 11 Jun 2020 16:17:29 +0100 Subject: [PATCH 08/13] fixed regression bug in text import fixed regression bug that caused textimport to crash. --- scribus/plugins/import/pdf/slaoutput.cpp | 57 ++++++++++++++---------- scribus/plugins/import/pdf/slaoutput.h | 14 +++--- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/scribus/plugins/import/pdf/slaoutput.cpp b/scribus/plugins/import/pdf/slaoutput.cpp index db9be34e1..5f9eb588e 100644 --- a/scribus/plugins/import/pdf/slaoutput.cpp +++ b/scribus/plugins/import/pdf/slaoutput.cpp @@ -20,6 +20,8 @@ for which a new license (GPL+exception) is in place. #include "util_math.h" #include + +#define DEBUG_TEXT_IMPORT namespace { // Compute the intersection of two paths while considering the fillrule of each of them. @@ -285,8 +287,6 @@ SlaOutputDev::SlaOutputDev(ScribusDoc* doc, QList *Elements, QStringL currentLayer = m_doc->activeLayer(); layersSetByOCG = false; importTextAsVectors = true; - - m_textFramework = new TextFramework(); } SlaOutputDev::~SlaOutputDev() @@ -295,7 +295,6 @@ SlaOutputDev::~SlaOutputDev() tmpSel->clear(); delete tmpSel; delete m_fontEngine; - delete m_textFramework; } /* get Actions not implemented by Poppler */ @@ -3365,7 +3364,7 @@ void SlaOutputDev::drawChar(GfxState* state, double x, double y, double dx, doub } } if (!importTextAsVectors) { // donm't render the char as vectors add it to an array so it can be rendred as a string - m_textFramework->addChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + m_textFramework.addChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); } } } @@ -3438,27 +3437,27 @@ void SlaOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, do void SlaOutputDev::beginTextObject(GfxState *state) { pushGroup(); - if (importTextAsVectors == false && !m_textFramework->activeTextRegion.textRegionLines.empty()) { - m_textFramework->addNewTextRegion(); + if (importTextAsVectors == false && !m_textFramework.activeTextRegion.textRegionLines.empty()) { + m_textFramework.addNewTextRegion(); } } void SlaOutputDev::endTextObject(GfxState *state) { - if (importTextAsVectors == false && !m_textFramework->activeTextRegion.textRegionLines.empty()) { + if (importTextAsVectors == false && !m_textFramework.activeTextRegion.textRegionLines.empty()) { // Add the last glyph to the textregion - QPointF glyphXY = m_textFramework->activeTextRegion.lastXY; - m_textFramework->activeTextRegion.lastXY.setX(m_textFramework->activeTextRegion.lastXY.x() - m_textFramework->activeTextRegion.glyphs.back().dx); - if (m_textFramework->activeTextRegion.addGlyphAtPoint(glyphXY, m_textFramework->activeTextRegion.glyphs.back()) == TextRegion::FrameworkLineTests::FAIL) { + QPointF glyphXY = m_textFramework.activeTextRegion.lastXY; + m_textFramework.activeTextRegion.lastXY.setX(m_textFramework.activeTextRegion.lastXY.x() - m_textFramework.activeTextRegion.glyphs.back().dx); + if (m_textFramework.activeTextRegion.addGlyphAtPoint(glyphXY, m_textFramework.activeTextRegion.glyphs.back()) == TextRegion::FrameworkLineTests::FAIL) { qDebug("FIXME: Rogue glyph detected, this should never happen because the copuror should move before glyphs in new regions are added."); } renderTextFrame(); - } else if (importTextAsVectors == false && !m_textFramework->activeTextRegion.textRegionLines.empty()) { + } else if (importTextAsVectors == false && !m_textFramework.activeTextRegion.textRegionLines.empty()) { qDebug("FIXME:Rogue textblock"); } - m_textFramework->setCharMode(TextFramework::AddCharMode::ADDFIRSTCHAR); + m_textFramework.setCharMode(TextFramework::AddCharMode::ADDFIRSTCHAR); // qDebug() << "SlaOutputDev::endTextObject"; if (!m_clipTextPath.isEmpty()) { @@ -3996,7 +3995,9 @@ TextRegion::FrameworkLineTests TextRegion::linearTest(QPointF point, bool xInLim { //TODO: character has gone suprtscript pass = FrameworkLineTests::STYLESUPERSCRIPT; +#ifdef DEBUG_TEXT_IMPORT qDebug() << "STYLESUPERSCRIPT point:" << point << " lastXY:" << lastXY << " lineBaseXY:" << lineBaseXY; +#endif } else if (adjunctGreater(point.y(),lastXY.y(),lineBaseXY.y())) { @@ -4004,13 +4005,17 @@ TextRegion::FrameworkLineTests TextRegion::linearTest(QPointF point, bool xInLim { // were back on track pass = FrameworkLineTests::STYLENORMALRETURN; +#ifdef DEBUG_TEXT_IMPORT qDebug() << "STYLENORMALRETURN"; +#endif } else { //TODO: this character has overflowed the height, or is still superscript just not so much pass = FrameworkLineTests::STYLESUPERSCRIPT; //could be STYLEBELOWBASELINE +#ifdef DEBUG_TEXT_IMPORT qDebug() << "STYLESUBSCRIPT point:" << point << " lastXY:" << lastXY << " lineBaseXY:" << lineBaseXY; +#endif //qDebug() << "STYLESUBSCRIPT: "; } } @@ -4328,18 +4333,18 @@ void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) void SlaOutputDev::updateTextPos(GfxState* state) { QPointF newPosition = QPointF(state->getCurX(), state->getCurY()); - TextRegion* activeTextRegion = &m_textFramework->activeTextRegion; + TextRegion* activeTextRegion = &m_textFramework.activeTextRegion; if ((activeTextRegion->isNew()) ) { activeTextRegion->textRegioBasenOrigin = newPosition; - m_textFramework->setCharMode(TextFramework::AddCharMode::ADDFIRSTCHAR); + m_textFramework.setCharMode(TextFramework::AddCharMode::ADDFIRSTCHAR); } else { // if we've will move to a new line or new text region then update the current text region with the last glyph, this ensures all textlines and textregions have terminating glyphs. - if (m_textFramework->isNewLineOrRegion(newPosition)) + if (m_textFramework.isNewLineOrRegion(newPosition)) { QPointF glyphPosition = activeTextRegion->lastXY; activeTextRegion->lastXY.setX(activeTextRegion->lastXY.x() - activeTextRegion->glyphs.back().dx); @@ -4348,7 +4353,9 @@ void SlaOutputDev::updateTextPos(GfxState* state) } else { - qDebug() << "Nedwline should be next"; +#ifdef DEBUG_TEXT_IMPORT + qDebug() << "Newline should be next"; +#endif }; } } @@ -4359,7 +4366,7 @@ void SlaOutputDev::updateTextPos(GfxState* state) renderTextFrame(); //Create and initilize a new TextRegion - m_textFramework->addNewTextRegion(); + m_textFramework.addNewTextRegion(); updateTextPos(state); } } @@ -4369,7 +4376,7 @@ void SlaOutputDev::renderTextFrame() //TODO: Implement, this should all in based the framework //qDebug() << "_flushText() m_doc->currentPage()->xOffset():" << m_doc->currentPage()->xOffset(); // Ignore empty strings - TextRegion* activeTextRegion = &m_textFramework->activeTextRegion; + auto activeTextRegion = &m_textFramework.activeTextRegion; if (activeTextRegion->glyphs.empty()) { // We don't clear the glyphs any more or at least until the whole page has been rendred glyphs.clear(); @@ -4390,8 +4397,10 @@ void SlaOutputDev::renderTextFrame() //FIXME: Use the framework for positioning not the first glyph qreal xCoor = m_doc->currentPage()->xOffset() + activeTextRegion->textRegioBasenOrigin.x(); qreal yCoor = m_doc->currentPage()->initialHeight() - (m_doc->currentPage()->yOffset() + (double)activeTextRegion->textRegioBasenOrigin.y() + activeTextRegion->lineSpacing); // don't know if y is top down or bottom up - double lineWidth = 0.0; + qreal lineWidth = 0.0; +#ifdef DEBUG_TEXT_IMPORT qDebug() << "rendering new frame at:" << xCoor << "," << yCoor << " With lineheight of: " << activeTextRegion->lineSpacing << "Height:" << activeTextRegion->maxHeight << " Width:" << activeTextRegion->maxWidth; +#endif /* colours don't get reset to CommonStrings::None often enough.*/ int z = m_doc->itemAdd(PageItem::TextFrame, PageItem::Rectangle, xCoor, yCoor, 40, 40, 0, CommonStrings::None, CommonStrings::None /* this->CurrColorStroke*/);//, PageItem::ItemKind::InlineItem); PageItem* textNode = m_doc->Items->at(z); @@ -4440,7 +4449,7 @@ void SlaOutputDev::renderTextFrame() //QString CurrColorText = getColor(state->getFillColorSpace(), state->getFillColor(), &shade); //TODO: replace this with the framework //applyTextStyleToCharStyle(pStyle.charStyle(), _glyphs[0].style->getFont().family(), CurrColorText, _glyphs[0].style->getFont().pointSizeF());// *_font_scaling); - CharStyle& cStyle = (CharStyle&)pStyle.charStyle(); + CharStyle& cStyle = static_cast(pStyle.charStyle()); cStyle.setScaleH(1000.0); cStyle.setScaleV(1000.0); cStyle.setHyphenChar(SpecialChars::BLANK.unicode()); @@ -4529,7 +4538,7 @@ void TextFramework::addNewTextRegion() void TextFramework::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { - (this->*(addChar))(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + (this->*(m_addChar))(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); } bool TextFramework::isNewLineOrRegion(QPointF newPosition) @@ -4543,7 +4552,8 @@ bool TextFramework::isNewLineOrRegion(QPointF newPosition) PdfGlyph TextFramework::AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { //qDebug() << "AddFirstChar() '" << u << " : " << uLen; - auto newGlyph = TextFramework::AddCharCommon(state, x, y, dx, dy, u, uLen); + PdfGlyph newGlyph = TextFramework::AddCharCommon(state, x, y, dx, dy, u, uLen); + activeTextRegion.glyphs.push_back(newGlyph); setCharMode(AddCharMode::ADDBASICCHAR); //only need to be called for the very first point @@ -4551,6 +4561,7 @@ PdfGlyph TextFramework::AddFirstChar(GfxState* state, double x, double y, double { qDebug("FIXME: Rogue glyph detected, this should never happen because the coursor should move before glyphs in new regions are added."); } + return newGlyph; } PdfGlyph TextFramework::AddCharCommon(GfxState* state, double x, double y, double dx, double dy, Unicode const* u, int uLen) { @@ -4571,7 +4582,7 @@ PdfGlyph TextFramework::AddCharCommon(GfxState* state, double x, double y, doubl PdfGlyph TextFramework::AddBasicChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { - auto newGlyph = AddCharCommon(state, x, y, dx, dy, u, uLen); + PdfGlyph newGlyph = AddCharCommon(state, x, y, dx, dy, u, uLen); activeTextRegion.lastXY = QPointF(x, y); activeTextRegion.glyphs.push_back(newGlyph); return newGlyph; diff --git a/scribus/plugins/import/pdf/slaoutput.h b/scribus/plugins/import/pdf/slaoutput.h index ac97ad48c..544f697c8 100644 --- a/scribus/plugins/import/pdf/slaoutput.h +++ b/scribus/plugins/import/pdf/slaoutput.h @@ -170,7 +170,7 @@ struct PdfGlyph class TextRegionLine { public: - qreal maxHeight = ; + qreal maxHeight = {}; //we can probably use maxHeight for this. qreal width = {}; int glyphIndex = {}; @@ -239,20 +239,20 @@ class TextFramework switch (mode) { case AddCharMode::ADDFIRSTCHAR: - addCharModal = AddFirstChar;; + addCharModal = &TextFramework::AddFirstChar; break; case AddCharMode::ADDBASICCHAR: - addCharModal = AddBasicChar; + addCharModal = &TextFramework::AddBasicChar; break; case AddCharMode::ADDCHARWITHNEWSTYLE: - addCharModal = AddCharWithNewStyle; + addCharModal = &TextFramework::AddCharWithNewStyle; break; case AddCharMode::ADDCHARWITHPREVIOUSSTYLE: - addCharModal = AddCharWithPreviousStyle; + addCharModal = &TextFramework::AddCharWithPreviousStyle; break; default: // FIXME: This should never happen, set to a default - addCharModal = AddBasicChar; + addCharModal = &TextFramework::AddBasicChar; } addCharModes[mode] = addCharModal; } @@ -503,7 +503,7 @@ class SlaOutputDev : public OutputDev QHash m_radioButtons; int m_actPage; //PDF Textbox framework - TextFramework* m_textFramework = nullptr; + TextFramework m_textFramework = {}; }; #endif From abb6beece6cb3d0b3f1782b34561a8c5f142a96e Mon Sep 17 00:00:00 2001 From: olivetthered <37796246+olivetthered@users.noreply.github.com> Date: Thu, 11 Jun 2020 21:46:47 +0100 Subject: [PATCH 09/13] all the changes following the review all the changes following the review: still todo function pointer tidy up and code formatting of irrelivant code put in a seperate patch --- scribus/plugins/import/pdf/slaoutput.cpp | 473 ++++++++++------------- scribus/plugins/import/pdf/slaoutput.h | 11 +- 2 files changed, 205 insertions(+), 279 deletions(-) diff --git a/scribus/plugins/import/pdf/slaoutput.cpp b/scribus/plugins/import/pdf/slaoutput.cpp index 5f9eb588e..a92e4bb79 100644 --- a/scribus/plugins/import/pdf/slaoutput.cpp +++ b/scribus/plugins/import/pdf/slaoutput.cpp @@ -3260,16 +3260,11 @@ void SlaOutputDev::updateFontForVector(GfxState *state) fontsrc->unref(); } -void SlaOutputDev::drawChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, POPPLER_CONST_082 Unicode* u, int uLen) +void SlaOutputDev::drawCharAsVector(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, POPPLER_CONST_082 Unicode* u, int uLen) { //qDebug() << "SlaOutputDev::drawChar x:" << x << "y:" << y << "dx:" << dx << "dy" << dy << "code:" << code; - //for importing text as glyphs double x1, y1, x2, y2; - //if (import_text_as_vectors) { updateFontForVector(state); //this sets m_font a splashfont - //TODO: add support for fonts when importing pdf text - //_updateFontForText(state); //and this sets font_style and has a lkot more detail about the font, both work on the same details from gfxfont - if (!m_font) return; @@ -3322,48 +3317,59 @@ void SlaOutputDev::drawChar(GfxState* state, double x, double y, double dx, doub FPointArray textPath; textPath.fromQPainterPath(qPath); FPoint wh = textPath.widthHeight(); - if (importTextAsVectors) - { - //qDebug() << "drawChar() "; - if (textRenderingMode > 3) - { - QTransform mm; - mm.scale(1, -1); - mm.translate(x, -y); - // Remember the glyph for later clipping - m_clipTextPath.addPath(m_ctm.map(mm.map(qPath))); - } - if ((textPath.size() > 3) && ((wh.x() != 0.0) || (wh.y() != 0.0)) && (textRenderingMode != 7)) + //qDebug() << "drawChar() "; + if (textRenderingMode > 3) + { + QTransform mm; + mm.scale(1, -1); + mm.translate(x, -y); + // Remember the glyph for later clipping + m_clipTextPath.addPath(m_ctm.map(mm.map(qPath))); + } + if ((textPath.size() > 3) && ((wh.x() != 0.0) || (wh.y() != 0.0)) && (textRenderingMode != 7)) + { + PageItem* textNode = nullptr; + + int z = m_doc->itemAdd(PageItem::Polygon, PageItem::Unspecified, xCoor, yCoor, 10, 10, 0, CommonStrings::None, CommonStrings::None); + textNode = m_doc->Items->at(z); + + // todo: merge this between vector and text implementations. + QTransform mm; + mm.scale(1, -1); + mm.translate(x, -y); + textPath.map(mm); + textPath.map(m_ctm); + textNode->PoLine = textPath.copy(); + setFillAndStrokeForPDF(state, textNode); + // Fill text rendering modes. See above + m_doc->adjustItemSize(textNode); + m_Elements->append(textNode); + if (m_groupStack.count() != 0) { - PageItem* textNode = nullptr; - - - int z = m_doc->itemAdd(PageItem::Polygon, PageItem::Unspecified, xCoor, yCoor, 10, 10, 0, CommonStrings::None, CommonStrings::None); - textNode = m_doc->Items->at(z); - - // todo: merge this between vector and text implementations. - - QTransform mm; - mm.scale(1, -1); - mm.translate(x, -y); - textPath.map(mm); - textPath.map(m_ctm); - textNode->PoLine = textPath.copy(); - setFillAndStrokeForPDF(state, textNode); - // Fill text rendering modes. See above - m_doc->adjustItemSize(textNode); - m_Elements->append(textNode); - if (m_groupStack.count() != 0) - { - m_groupStack.top().Items.append(textNode); - applyMask(textNode); - } + m_groupStack.top().Items.append(textNode); + applyMask(textNode); } - delete fontPath; } + delete fontPath; + } - if (!importTextAsVectors) { // donm't render the char as vectors add it to an array so it can be rendred as a string + } +} + +void SlaOutputDev::drawChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, POPPLER_CONST_082 Unicode* u, int uLen) +{ + if(importTextAsVectors) + drawCharAsVector(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + else + { + // TODO Implement the clipping operations. At least the characters are shown. + int textRenderingMode = state->getRender(); + // Invisible or only used for clipping + if (textRenderingMode == 3) + return; + if (textRenderingMode < 8) + { m_textFramework.addChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); } } @@ -3438,7 +3444,7 @@ void SlaOutputDev::beginTextObject(GfxState *state) { pushGroup(); if (importTextAsVectors == false && !m_textFramework.activeTextRegion.textRegionLines.empty()) { - m_textFramework.addNewTextRegion(); + m_textFramework.addTextRegion(); } } @@ -3480,13 +3486,8 @@ void SlaOutputDev::endTextObject(GfxState *state) ite = m_doc->groupObjectsSelection(tmpSel); else ite = gElements.Items.first(); - ite->setGroupClipping(false); - if (importTextAsVectors == false) { - ite->setFillTransparency(1.0 - state->getFillOpacity()); - } - else { - ite->setFillTransparency(1.0 - state->getFillOpacity()); - } + ite->setGroupClipping(false); + ite->setFillTransparency(1.0 - state->getFillOpacity()); ite->setFillBlendmode(getBlendMode(state)); for (int as = 0; as < tmpSel->count(); ++as) { @@ -3927,29 +3928,32 @@ bool SlaOutputDev::checkClip() return ret; } -// TODO: remove mutual dependencies and move the render engine into it's own cc and header files because it's getting hard to navigate the code base. -// PDF never deviates from the line when it comes to colenear -bool TextRegion::coLinera(qreal a, qreal b) + +/* +In geometry, collinearity of a set of points is the property of their lying on a single line. A set of points with this property is said to be collinear. +In greater generality, the term has been used for aligned objects, that is, things being "in a line" or "in a row". + PDF never deviates from the line when it comes to collinear, but allow for 1pixel of divergence +*/ +bool TextRegion::collinear(qreal a, qreal b) { return abs(a - b) < 1 ? true : false; } -// like _colenia but we allow a deviation of upto +-2 rejion font text widths -bool TextRegion::closeToX(qreal x1, qreal x2) +// like collinear but we allow a deviation of up to +-2 rejoin font text widths +bool TextRegion::isCloseToX(qreal x1, qreal x2) { - //TODO: return abs(x1 - x2) <= coreText.mWidth() * 2 ? true : false; - return abs(x2 - x1) <= lineSpacing * 6 ? true : abs(x1 - this->textRegioBasenOrigin.x()) <= lineSpacing ? true: false; //allow infinate overrun but only one char width underrun - //return true; + //FIXME: This should use the char width not linespacing which is y + return (abs(x2 - x1) <= lineSpacing * 6) || (abs(x1 - this->textRegioBasenOrigin.x()) <= lineSpacing); } -// like _colenia but we allow a deviation of upto 2 rejion font linespaces, but in one direction and half a line space in the other direction -bool TextRegion::closeToY(qreal y1, qreal y2) +// like collinear but we allow a deviation of up to 3 rejoin font linespaces, but in one direction and half a line space in the other direction +bool TextRegion::isCloseToY(qreal y1, qreal y2) { - //FIXME: Actually test the correctg magnitudes not the abs value. There shound be a parameter in the ui to set the matching tollerance but hard code to allow 3 linespaces difference before we say that the y is out of scope. - return (y2 - y1) >= 0 && y2 - y1 <= lineSpacing * 3 ? true : false; + //FIXME: Actually test the correct magnitudes not the abs value. There should be a parameter in the ui to set the matching tolerance but hard code to allow 3 linespaces difference before we say that the y is out of scope. + return (y2 - y1) >= 0 && y2 - y1 <= lineSpacing * 3; } -// lesss than the last y value but bot more than the line spacing less, could also use the base line of the last line to be more accurate +// less than the last y value but bot more than the line spacing less, could also use the base line of the last line to be more accurate bool TextRegion::adjunctLesser(qreal testY, qreal lastY, qreal baseY) { return (testY > lastY @@ -3957,7 +3961,7 @@ bool TextRegion::adjunctLesser(qreal testY, qreal lastY, qreal baseY) && lastY <= baseY + lineSpacing); } -// lesss than the last y value but bot more than the line spacing less, could also use the base line of the last line to be more accurate +// less than the last y value but bot more than the line spacing less, could also use the base line of the last line to be more accurate bool TextRegion::adjunctGreater(qreal testY, qreal lastY, qreal baseY) { return (testY <= lastY @@ -3970,10 +3974,10 @@ TextRegion::FrameworkLineTests TextRegion::linearTest(QPointF point, bool xInLim //TODO: add a FIRSTPOINT result as well FrameworkLineTests pass = FrameworkLineTests::FAIL; - //FIXME: I think thjis should be using baseXY not lastXY - if (coLinera(point.y(), lastXY.y())) + //FIXME: I think this should be using baseXY not lastXY + if (collinear(point.y(), lastXY.y())) { - if (coLinera(point.x(), lastXY.x())) + if (collinear(point.x(), lastXY.x())) { pass = FrameworkLineTests::FIRSTPOINT; #ifdef DEBUG_TEXT_IMPORT @@ -3983,17 +3987,17 @@ TextRegion::FrameworkLineTests TextRegion::linearTest(QPointF point, bool xInLim else if (xInLimits) { // this is for item ##16 - // ok, this should only happen when a new glyph is added not when the cursor position is set, but in both cases we can call extend by the point and set the glyph to the current glyph checking that it's not a duplicate + // OK, this should only happen when a new glyph is added not when the cursor position is set, but in both cases we can call extend by the point and set the glyph to the current glyph checking that it's not a duplicate //TODO: textRegionLines.end().extend(point).setGlyph(newGlyph); pass = FrameworkLineTests::SAMELINE; #ifdef DEBUG_TEXT_IMPORT qDebug() << "SAMELINE " << point << " lastxy:"<< lastXY; #endif } - } // else see if y is a bit too much off thelastyx line to be linear + } // else see if y is a bit too much off the lastyx line to be linear else if (adjunctLesser(point.y(), lastXY.y(), lineBaseXY.y())) { - //TODO: character has gone suprtscript + //TODO: character has gone superscript pass = FrameworkLineTests::STYLESUPERSCRIPT; #ifdef DEBUG_TEXT_IMPORT qDebug() << "STYLESUPERSCRIPT point:" << point << " lastXY:" << lastXY << " lineBaseXY:" << lineBaseXY; @@ -4001,7 +4005,7 @@ TextRegion::FrameworkLineTests TextRegion::linearTest(QPointF point, bool xInLim } else if (adjunctGreater(point.y(),lastXY.y(),lineBaseXY.y())) { - if (coLinera(point.y(), lineBaseXY.y())) //PDF never deviates from the line when it comes to colenear + if (collinear(point.y(), lineBaseXY.y())) //PDF never deviates from the line when it comes to collinear { // were back on track pass = FrameworkLineTests::STYLENORMALRETURN; @@ -4020,13 +4024,13 @@ TextRegion::FrameworkLineTests TextRegion::linearTest(QPointF point, bool xInLim } } else { - //TODO: We need to calculate things like new parargraphs and left hand justification - if (closeToX(point.x(), textRegioBasenOrigin.x())) + //TODO: We need to calculate things like new paragraphs and left hand justification + if (isCloseToX(point.x(), textRegioBasenOrigin.x())) { - if (closeToY(point.y(), lastXY.y()) && !coLinera(point.y(), lastXY.y())) + if (isCloseToY(point.y(), lastXY.y()) && !collinear(point.y(), lastXY.y())) { - //TODO: We need to calculate things like new parargraphs and left hand justification - if ((textRegionLines.size() >= 2)) //TODO: Need to setup some parameters replating to width matching, they mainly relate to justfication && closeToX(textRegionLines[textRegionLines.size() - 2].baseOrigin.x() + textRegionLines[textRegionLines.size() - 2].width, maxWidth)) + //TODO: We need to calculate things like new paragraphs and left hand justification + if ((textRegionLines.size() >= 2)) //TODO: Need to setup some parameters relating to width matching, they mainly relate to justification && closeToX(textRegionLines[textRegionLines.size() - 2].baseOrigin.x() + textRegionLines[textRegionLines.size() - 2].width, maxWidth)) { //TODO: add a new line and update the deltas pass = FrameworkLineTests::NEWLINE; @@ -4054,7 +4058,7 @@ TextRegion::FrameworkLineTests TextRegion::linearTest(QPointF point, bool xInLim return pass; } -// Just perform some basic checks to see if newPoint can reasonably be asscribed to the current textframe. +// Just perform some basic checks to see if newPoint can reasonably be ascribed to the current textframe. TextRegion::FrameworkLineTests TextRegion::isRegionConcurrent(QPointF newPoint) { if (glyphs.empty()) @@ -4064,20 +4068,8 @@ TextRegion::FrameworkLineTests TextRegion::isRegionConcurrent(QPointF newPoint) } //TODO: I need to write down which ones we want so I can work it all out, for now just some basic fuzzy matching support. - bool xInLimits = false; - if (closeToX(newPoint.x(), lastXY.x())) - { - xInLimits = true; - } - else - { - qDebug() << "X out of limits " << lastXY << " : " << newPoint; - } - bool yInLimits = false; - if (closeToY(newPoint.y(), lastXY.y())) - { - yInLimits = true; - } + bool xInLimits = isCloseToX(newPoint.x(), lastXY.x()); + bool yInLimits = isCloseToY(newPoint.y(), lastXY.y()); FrameworkLineTests pass = linearTest(newPoint, xInLimits, yInLimits); return pass; } @@ -4087,7 +4079,7 @@ TextRegion::FrameworkLineTests TextRegion::moveToPoint(QPointF newPoint) { // //qDebug() << "moveToPoint: " << newPoint; - // Do some initilization if we are in a new text region + // Do some initialization if we are in a new text region // we could also update these if glyphindex = -1; if (glyphs.empty()) { @@ -4097,95 +4089,64 @@ TextRegion::FrameworkLineTests TextRegion::moveToPoint(QPointF newPoint) } //TODO: I need to write down which ones we want so I can work it all out, for now just some basic fuzzy matching support. - //TODO: x limiting should be different for moving as opposed to adding a new glyph because moiving is due to a discontinuity in glyphs - bool xInLimits = false; - if (closeToX(newPoint.x(), lastXY.x())) - { - //qDebug() << "newPoint6: " << newPoint; - xInLimits = true; - } - bool yInLimits = false; - if (closeToY(newPoint.y(), lastXY.y())) - { - //qDebug() << "newPoint7: " << newPoint; - yInLimits = true; - } + //TODO: x limiting should be different for moving as opposed to adding a new glyph because moving is due to a discontinuity in glyphs //qDebug() << "newPoint8: " << newPoint; - FrameworkLineTests pass = linearTest(newPoint, xInLimits, yInLimits); + FrameworkLineTests pass = isRegionConcurrent(newPoint); //TODO: need to check to see if we are creating a new paragraph or not. basically if the cursor is returned to x origin before it reached x width. this could be returned as part of a matrix by linearTest that specifies exactly how the test ws passed. maybew return an enum with either the mode that passed or a failure value - if (pass != FrameworkLineTests::FAIL) - { - //create new lines and line segments depending upon the moide of the movement. - TextRegionLine *textRegionLine = nullptr; + if (pass == FrameworkLineTests::FAIL) + return pass; - if (pass == FrameworkLineTests::NEWLINE || pass == FrameworkLineTests::FIRSTPOINT) - { - //qDebug() << "Newline: "; - if (pass == FrameworkLineTests::FIRSTPOINT) - { - if (textRegionLines.empty()) { - textRegionLines.push_back(TextRegionLine()); - } - } - else - { - textRegionLines.push_back(TextRegionLine()); - } - textRegionLine = &textRegionLines.back(); - textRegionLine->baseOrigin = newPoint; - if (pass == FrameworkLineTests::NEWLINE) - { - textRegionLine->maxHeight = abs(newPoint.y() - lastXY.y()); - if (textRegionLines.size() == 2) - { - lineSpacing = abs(newPoint.y() - lastXY.y()) + 1; - //qDebug() << "setting lineSpacing to:" << lineSpacing; - } - } - } + //create new lines and line segments depending upon the mode of the movement. + TextRegionLine *textRegionLine = nullptr; - textRegionLine = &textRegionLines.back(); - if ((pass == FrameworkLineTests::FIRSTPOINT && textRegionLine->segments.empty()) || pass == FrameworkLineTests::NEWLINE || pass != FrameworkLineTests::FIRSTPOINT && textRegionLine->segments[0].glyphIndex != textRegionLine->glyphIndex) - { - TextRegionLine newSegment = TextRegionLine(); - textRegionLine->segments.push_back(newSegment); - } - TextRegionLine* segment = &textRegionLine->segments.back(); - segment->baseOrigin = newPoint; - if (pass == FrameworkLineTests::STYLESUPERSCRIPT) - { - segment->maxHeight = abs(lineSpacing - (newPoint.y() - lastXY.y())); - - } - else - { - segment->maxHeight = textRegionLines.back().maxHeight; - } + if (pass == FrameworkLineTests::NEWLINE || pass == FrameworkLineTests::FIRSTPOINT) + { + //qDebug() << "Newline: "; + if (pass != FrameworkLineTests::FIRSTPOINT || textRegionLines.empty()) + textRegionLines.push_back(TextRegionLine()); - if (pass != FrameworkLineTests::NEWLINE && pass != FrameworkLineTests::FIRSTPOINT) + textRegionLine = &textRegionLines.back(); + textRegionLine->baseOrigin = newPoint; + if (pass == FrameworkLineTests::NEWLINE) { - textRegionLines.back().segments.back().width = abs(textRegionLines.back().segments.back().baseOrigin.x() - newPoint.x()); - textRegionLine = &textRegionLines.back(); - textRegionLine->width = abs(textRegionLine->baseOrigin.x() - newPoint.x()); + textRegionLine->maxHeight = abs(newPoint.y() - lastXY.y()); + if (textRegionLines.size() == 2) + lineSpacing = abs(newPoint.y() - lastXY.y()) + 1; + //qDebug() << "setting lineSpacing to:" << lineSpacing; } - - maxHeight = abs(textRegioBasenOrigin.y() - newPoint.y()) > maxHeight ? abs(textRegioBasenOrigin.y() - newPoint.y()) : maxHeight; - lastXY = newPoint; } - else + + textRegionLine = &textRegionLines.back(); + if ((pass == FrameworkLineTests::FIRSTPOINT && textRegionLine->segments.empty()) || pass == FrameworkLineTests::NEWLINE || pass != FrameworkLineTests::FIRSTPOINT && textRegionLine->segments[0].glyphIndex != textRegionLine->glyphIndex) { - //qDebug() << "movetopoint failed"; + TextRegionLine newSegment = TextRegionLine(); + textRegionLine->segments.push_back(newSegment); } + TextRegionLine* segment = &textRegionLine->segments.back(); + segment->baseOrigin = newPoint; + segment->maxHeight = (pass == FrameworkLineTests::STYLESUPERSCRIPT) ? + abs(lineSpacing - (newPoint.y() - lastXY.y())) : + textRegionLines.back().maxHeight; + if (pass != FrameworkLineTests::NEWLINE && pass != FrameworkLineTests::FIRSTPOINT) + { + textRegionLines.back().segments.back().width = abs(textRegionLines.back().segments.back().baseOrigin.x() - newPoint.x()); + textRegionLine = &textRegionLines.back(); + textRegionLine->width = abs(textRegionLine->baseOrigin.x() - newPoint.x()); + } + + maxHeight = abs(textRegioBasenOrigin.y() - newPoint.y()) > maxHeight ? abs(textRegioBasenOrigin.y() - newPoint.y()) : maxHeight; + lastXY = newPoint; + return pass; } -//TODO:, extract some font heights instesad of using dx all the time +//TODO:, extract some font heights instead of using dx all the time TextRegion::FrameworkLineTests TextRegion::addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph newGlyph) { QPointF movedGlyphPoint = QPointF(newGlyphPoint.x() + newGlyph.dx, newGlyphPoint.y() + newGlyph.dy); //qDebug() << "addGlyphAtPoint start" << newGlyphPoint << " glyph:"<< new_glyph.code; - //FIXME: There should be no need for testing the scope when adding a new glyph because moveto should have been called first but leave it in for now to catch any errors in the logic + //FIXME: There should be no need for testing the scope when adding a new glyph because move to should have been called first but leave it in for now to catch any errors in the logic if (glyphs.size() == 1) { // FIXME: do a propper lookup of the height @@ -4194,67 +4155,48 @@ TextRegion::FrameworkLineTests TextRegion::addGlyphAtPoint(QPointF newGlyphPoint lastXY = newGlyphPoint; lineBaseXY = newGlyphPoint; } - bool xInLimits = false; - if (closeToX(newGlyphPoint.x(), lastXY.x())) - { - xInLimits = true; - } - bool yInLimits = false; - if (closeToY(newGlyphPoint.y(), lastXY.y())) - { - yInLimits = true; + // TODO: add and move may want different versions of isCloseToX, but for now use the same generic function for both + FrameworkLineTests pass = isRegionConcurrent(newGlyphPoint); + if (pass == FrameworkLineTests::FAIL) + return pass; + + maxHeight = abs(textRegioBasenOrigin.y() - movedGlyphPoint.y()) + lineSpacing > maxHeight ? abs(textRegioBasenOrigin.y() - movedGlyphPoint.y()) + lineSpacing : maxHeight; + //move to deals with setting newlines and segments, all we have to do is populate them with the parameters the glyph gives us such as it's width and height and set the glyph index for the newlines and segments + + TextRegionLine* textRegionLine = &textRegionLines.back(); + if (pass == FrameworkLineTests::NEWLINE || pass == FrameworkLineTests::FIRSTPOINT) { + textRegionLine->glyphIndex = glyphs.size() - 1; + textRegionLine->baseOrigin = QPointF(textRegioBasenOrigin.x(), newGlyphPoint.y()); } - FrameworkLineTests pass = linearTest(newGlyphPoint, xInLimits, yInLimits); - if (pass != FrameworkLineTests::FAIL) - { - maxHeight = abs(textRegioBasenOrigin.y() - movedGlyphPoint.y()) + lineSpacing > maxHeight ? abs(textRegioBasenOrigin.y() - movedGlyphPoint.y()) + lineSpacing : maxHeight; - //moveto deals with setting newlines and segments, all we hve to do is populatit them with the parameters tyhe glyph gives us such as it's width and height and set the glyph index for the newlines and segments - TextRegionLine* textRegionLine = &textRegionLines.back(); - if (pass == FrameworkLineTests::NEWLINE || pass == FrameworkLineTests::FIRSTPOINT) { - textRegionLine->glyphIndex = glyphs.size() - 1; - textRegionLine->baseOrigin = QPointF(textRegioBasenOrigin.x(), newGlyphPoint.y()); - } + TextRegionLine *segment = &textRegionLine->segments.back(); + segment->width = abs(movedGlyphPoint.x() - segment->baseOrigin.x()); + segment->glyphIndex = glyphs.size() - 1; + qreal thisHeight = textRegionLines.size() > 1 ? + abs(newGlyphPoint.y() - textRegionLines[textRegionLines.size() - 2].baseOrigin.y()) : + newGlyph.dx; - TextRegionLine *segment = &textRegionLine->segments.back(); - segment->width = abs(movedGlyphPoint.x() - segment->baseOrigin.x()); - segment->glyphIndex = glyphs.size() - 1; - qreal thisHeight = 0; - if (textRegionLines.size() > 1) - { - thisHeight = abs(newGlyphPoint.y() - textRegionLines[textRegionLines.size() - 2].baseOrigin.y()); - } - else - { - thisHeight = newGlyph.dx; - } - segment->maxHeight = thisHeight > segment->maxHeight ? thisHeight : segment->maxHeight; - textRegionLine->maxHeight = textRegionLine->maxHeight > thisHeight ? textRegionLine->maxHeight : thisHeight; - textRegionLine->width = abs(movedGlyphPoint.x() - textRegionLine->baseOrigin.x()); + segment->maxHeight = thisHeight > segment->maxHeight ? thisHeight : segment->maxHeight; + textRegionLine->maxHeight = textRegionLine->maxHeight > thisHeight ? textRegionLine->maxHeight : thisHeight; + textRegionLine->width = abs(movedGlyphPoint.x() - textRegionLine->baseOrigin.x()); + + maxWidth = textRegionLine->width > maxWidth ? textRegionLine->width : maxWidth; + if (textRegionLine->segments.size() == 1) + lineBaseXY = textRegionLine->baseOrigin; + + lastXY = movedGlyphPoint; - maxWidth = textRegionLine->width > maxWidth ? textRegionLine->width : maxWidth; - if (textRegionLine->segments.size() == 1) - { - lineBaseXY = textRegionLine->baseOrigin; - } - lastXY = movedGlyphPoint; - } - else - { - qDebug() << "addGlyphAtPoint failed, this should never happen"; - } return pass; } -void TextRegion::renderToTextFrame(PageItem* textNode, ParagraphStyle& pStyle) +void TextRegion::renderToTextFrame(PageItem* textNode) { // nothing clever, just get all the body text in one lump and update the text frame textNode->setWidthHeight(this->maxWidth, this->maxHeight); QString bodyText = ""; for (int glyphIndex = this->textRegionLines.begin()->glyphIndex; glyphIndex <= this->textRegionLines.back().segments.back().glyphIndex; glyphIndex++) - { bodyText += glyphs[glyphIndex].code; - } + textNode->itemText.insertChars(bodyText); textNode->frameTextEnd(); } @@ -4285,9 +4227,9 @@ void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) { CurrColorFill = getColor(state->getFillColorSpace(), state->getFillColor(), &CurrFillShade); - if (textNode->isTextFrame()) { //fill colour sets the background colour for the frame not the fill colour fore the text - textNode->setFillTransparency(1.0 - (state->getFillOpacity() > state->getStrokeOpacity() ? state->getFillOpacity() : state->getStrokeOpacity())); - textNode->setLineTransparency(1.0); // this ssets the transparency of the textbox border and we don't want to see it + if (textNode->isTextFrame()) { + textNode->setFillTransparency(1.0 - (state->getFillOpacity() > state->getStrokeOpacity() ? state->getFillOpacity() : state->getStrokeOpacity())); //fill colour sets the background colour for the frame not the fill colour fore the text + textNode->setLineTransparency(1.0); // this sets the transparency of the textbox border and we don't want to see it textNode->setFillColor(CommonStrings::None); textNode->setLineColor(CommonStrings::None); textNode->setLineWidth(0);//line width doesn't effect drawing text, it creates a bounding box state->getTransformedLineWidth()); @@ -4306,10 +4248,10 @@ void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) if (textRenderingMode == 1 || textRenderingMode == 2 || textRenderingMode == 5 || textRenderingMode == 6) { CurrColorStroke = getColor(state->getStrokeColorSpace(), state->getStrokeColor(), &CurrStrokeShade); - if (textNode->isTextFrame()) { //fil;l colour sets the background colour for the frame not the fill colour fore the text + if (textNode->isTextFrame()) { //fill colour sets the background colour for the frame not the fill colour fore the text textNode->setFillTransparency(1.0 - (state->getFillOpacity() > state->getStrokeOpacity() ? state->getFillOpacity() : state->getStrokeOpacity())); textNode->setLineTransparency(1.0); // this sets the transparency of the textbox border and we don't want to see it - textNode->setFillColor(CommonStrings::None); //TODO: Check if we ov erride the stroke colour with the fil,l colour when threre is a choice + textNode->setFillColor(CommonStrings::None); //TODO: Check if we override the stroke colour with the fill colour when there is a choice textNode->setLineColor(CommonStrings::None); textNode->setLineWidth(0);//line width doesn't effect drawing text, it creates a bounding box state->getTransformedLineWidth()); textNode->setFillBlendmode(getBlendMode(state)); @@ -4335,7 +4277,7 @@ void SlaOutputDev::updateTextPos(GfxState* state) QPointF newPosition = QPointF(state->getCurX(), state->getCurY()); TextRegion* activeTextRegion = &m_textFramework.activeTextRegion; - if ((activeTextRegion->isNew()) + if (activeTextRegion->isNew() ) { activeTextRegion->textRegioBasenOrigin = newPosition; @@ -4348,25 +4290,22 @@ void SlaOutputDev::updateTextPos(GfxState* state) { QPointF glyphPosition = activeTextRegion->lastXY; activeTextRegion->lastXY.setX(activeTextRegion->lastXY.x() - activeTextRegion->glyphs.back().dx); - if (activeTextRegion->addGlyphAtPoint(glyphPosition, activeTextRegion->glyphs.back()) == TextRegion::FrameworkLineTests::FAIL) { + if (activeTextRegion->addGlyphAtPoint(glyphPosition, activeTextRegion->glyphs.back()) == TextRegion::FrameworkLineTests::FAIL) qDebug("FIXME: Rogue glyph detected, this should never happen because the cursor should move before glyphs in new regions are added."); - } - else - { -#ifdef DEBUG_TEXT_IMPORT +#ifdef DEBUG_TEXT_IMPORT + else qDebug() << "Newline should be next"; #endif - }; } } TextRegion::FrameworkLineTests lineTestResult = activeTextRegion->moveToPoint(newPosition); if (lineTestResult == TextRegion::FrameworkLineTests::FAIL) { - // FIXME: render the textframe, this should be done after the document has finished loading the current page so all the layout fixups can be put inplace first + // FIXME: render the textframe, this should be done after the document has finished loading the current page so all the layout fix-ups can be put in-place first renderTextFrame(); //Create and initilize a new TextRegion - m_textFramework.addNewTextRegion(); + m_textFramework.addTextRegion(); updateTextPos(state); } } @@ -4378,23 +4317,9 @@ void SlaOutputDev::renderTextFrame() // Ignore empty strings auto activeTextRegion = &m_textFramework.activeTextRegion; if (activeTextRegion->glyphs.empty()) - { - // We don't clear the glyphs any more or at least until the whole page has been rendred glyphs.clear(); + // We don't clear the glyphs any more or at least until the whole page has been rendered glyphs.clear(); return; - } - //FIXME: This is redundant, should be using the framework not the first glyph. - //const PdfGlyph& first_glyph = activeTextRegion.glyphs[activeTextRegion.textRegionLines[0].glyphIndex];// (*i); - // TODO: Use the framework for this, not currently supported - /* - int render_mode = first_glyph.render_mode; - // Ignore invisible characters - if (render_mode == 3) { - // We don't clear the glyphs any more or at least until the whole page has been rendred //_glyphs.clear(); - return; - } - */ - //FIXME: Use the framework for positioning not the first glyph qreal xCoor = m_doc->currentPage()->xOffset() + activeTextRegion->textRegioBasenOrigin.x(); qreal yCoor = m_doc->currentPage()->initialHeight() - (m_doc->currentPage()->yOffset() + (double)activeTextRegion->textRegioBasenOrigin.y() + activeTextRegion->lineSpacing); // don't know if y is top down or bottom up qreal lineWidth = 0.0; @@ -4410,28 +4335,28 @@ void SlaOutputDev::renderTextFrame() pStyle.setLineSpacingMode(pStyle.AutomaticLineSpacing); pStyle.setHyphenationMode(pStyle.AutomaticHyphenation); - // TODO: Implement thease using the framework + // TODO: Implement these using the framework finishItem(textNode); - // FIXME: Implement thease using the framework + // FIXME: Implement these using the framework //_setFillAndStrokeForPdf(state, text_node); - //FIXME: Here's some dummy code for now with sednsible defaults, looks like state wasn't even needed - if (true) - { - textNode->ClipEdited = true; - textNode->FrameType = 3; - textNode->setLineEnd(PLineEnd); - textNode->setLineJoin(PLineJoin); - textNode->setTextFlowMode(PageItem::TextFlowDisabled); - //textNode->setFillTransparency(1.0); - textNode->setLineTransparency(1.0); // this ssets the transparency of the textbox border and we don't want to see it - textNode->setFillColor(CommonStrings::None); - textNode->setLineColor(CommonStrings::None); - textNode->setLineWidth(0);//line width doesn't effect drawing text, it creates a bounding box state->getTransformedLineWidth()); - textNode->setFillShade(CurrFillShade); - } - - // Set text matrix... This need to be done so that the globaal world view that we rite out glyphs to is transformed correctly by the context matrix for each glyph, possibly anyhow. - /* FIXME: Setting the text matrix isn't supp;orted at the moment + //FIXME: Here's some dummy code for now with sensible defaults, looks like state wasn't even needed + + textNode->ClipEdited = true; + textNode->FrameType = 3; + textNode->setLineEnd(PLineEnd); + textNode->setLineJoin(PLineJoin); + textNode->setTextFlowMode(PageItem::TextFlowDisabled); + //textNode->setFillTransparency(1.0); + textNode->setLineTransparency(1.0); // this sets the transparency of the textbox border and we don't want to see it + textNode->setFillColor(CommonStrings::None); + textNode->setLineColor(CommonStrings::None); + textNode->setLineWidth(0);//line width doesn't effect drawing text, it creates a bounding box state->getTransformedLineWidth()); + textNode->setFillShade(CurrFillShade); + + + // Oliver Stieber 2020-06-11 Set text matrix... This need to be done so that the global world view that we rite out glyphs to is transformed correctly by the context matrix for each glyph, possibly anyhow. + // needs the way in whicvh we are handeling transformations for the page to be more concrete beofre this code can be implemented either here or somewhere else + /* FIXME: Setting the text matrix isn't supported at the moment QTransform text_transform(_text_matrix); text_transform.setMatrix(text_transform.m11(), text_transform.m12(), 0, text_transform.m21(), text_transform.m22(), 0, @@ -4442,7 +4367,7 @@ void SlaOutputDev::renderTextFrame() text_node->setAttribute("transform", transform); g_free(transform); */ - /*set the default charstyle to the style of the glyph, this needss fleshing out a little */ + /*set the default charstyle to the style of the glyph, this needs fleshing out a little */ int shade = 100; //TODO: This needs to come from the framework @@ -4456,7 +4381,7 @@ void SlaOutputDev::renderTextFrame() textNode->itemText.setDefaultStyle(pStyle); textNode->invalid = true; - activeTextRegion->renderToTextFrame(textNode, pStyle); + activeTextRegion->renderToTextFrame(textNode); //FIXME: Paragraphs need to be implemented properly this needs to be applied to the charstyle of the default pstyle textNode->itemText.insertChars(SpecialChars::PARSEP, true); @@ -4464,7 +4389,7 @@ void SlaOutputDev::renderTextFrame() FPointArray boundingBoxShape; boundingBoxShape.resize(0); boundingBoxShape.svgInit(); - + //doubles to create a shape, it's 100% textframe width by 100% textframe height double bbosdoubles[32] = { 0,0 ,0,0 ,100,0 @@ -4529,7 +4454,7 @@ TextFramework::~TextFramework() { } -void TextFramework::addNewTextRegion() +void TextFramework::addTextRegion() { activeTextRegion = TextRegion(); m_textRegions.push_back(activeTextRegion); @@ -4543,10 +4468,10 @@ void TextFramework::addChar(GfxState* state, double x, double y, double dx, doub bool TextFramework::isNewLineOrRegion(QPointF newPosition) { - return (activeTextRegion.coLinera(activeTextRegion.lastXY.y(), activeTextRegion.textRegionLines.back().baseOrigin.y()) && - !activeTextRegion.coLinera(newPosition.y(), activeTextRegion.lastXY.y())) - || (activeTextRegion.coLinera(newPosition.y(), activeTextRegion.lastXY.y()) - && !activeTextRegion.closeToX(newPosition.x(), activeTextRegion.lastXY.x())); + return (activeTextRegion.collinear(activeTextRegion.lastXY.y(), activeTextRegion.textRegionLines.back().baseOrigin.y()) && + !activeTextRegion.collinear(newPosition.y(), activeTextRegion.lastXY.y())) + || (activeTextRegion.collinear(newPosition.y(), activeTextRegion.lastXY.y()) + && !activeTextRegion.isCloseToX(newPosition.x(), activeTextRegion.lastXY.x())); } PdfGlyph TextFramework::AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) @@ -4557,10 +4482,9 @@ PdfGlyph TextFramework::AddFirstChar(GfxState* state, double x, double y, double setCharMode(AddCharMode::ADDBASICCHAR); //only need to be called for the very first point - if (activeTextRegion.addGlyphAtPoint(QPointF(x, y), newGlyph) == TextRegion::FrameworkLineTests::FAIL) - { + auto success = activeTextRegion.addGlyphAtPoint(QPointF(x, y), newGlyph); + if(success == TextRegion::FrameworkLineTests::FAIL) qDebug("FIXME: Rogue glyph detected, this should never happen because the coursor should move before glyphs in new regions are added."); - } return newGlyph; } PdfGlyph TextFramework::AddCharCommon(GfxState* state, double x, double y, double dx, double dy, Unicode const* u, int uLen) @@ -4573,7 +4497,7 @@ PdfGlyph TextFramework::AddCharCommon(GfxState* state, double x, double y, doubl // Convert the character to UTF-16 since that's our SVG document's encoding for (int i = 0; i < uLen; i++) { - newGlyph.code = (char16_t)u[i]; + newGlyph.code = static_cast(u[i]); } newGlyph.rise = state->getRise(); @@ -4587,7 +4511,7 @@ PdfGlyph TextFramework::AddBasicChar(GfxState* state, double x, double y, double activeTextRegion.glyphs.push_back(newGlyph); return newGlyph; } - +/*TODO: Currently not implemented, just stub code*/ PdfGlyph TextFramework::AddCharWithNewStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { //qDebug() << "AddCharWithNewStyle() '" << u << " : " << uLen; @@ -4596,6 +4520,7 @@ PdfGlyph TextFramework::AddCharWithNewStyle(GfxState* state, double x, double y, return newGlyph; } +/*TODO: Currently not implemented, just stub code*/ PdfGlyph TextFramework::AddCharWithPreviousStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { //qDebug() << "AddCharWithPreviousStyle() '" << u << " : " << uLen; diff --git a/scribus/plugins/import/pdf/slaoutput.h b/scribus/plugins/import/pdf/slaoutput.h index 544f697c8..39b91e276 100644 --- a/scribus/plugins/import/pdf/slaoutput.h +++ b/scribus/plugins/import/pdf/slaoutput.h @@ -201,16 +201,16 @@ class TextRegion qreal maxWidth = {}; QPointF lineBaseXY = QPointF({ }, { }); //updated with the best match left value from all the textRegionLines and the best bottom value from the textRegionLines.segments; QPointF lastXY = QPointF({}, {}); - static bool coLinera(qreal a, qreal b); - bool closeToX(qreal x1, qreal x2); - bool closeToY(qreal y1, qreal y2); + static bool collinear(qreal a, qreal b); + bool isCloseToX(qreal x1, qreal x2); + bool isCloseToY(qreal y1, qreal y2); bool adjunctLesser(qreal testY, qreal lastY, qreal baseY); bool adjunctGreater(qreal testY, qreal lastY, qreal baseY); TextRegion::FrameworkLineTests linearTest(QPointF point, bool xInLimits, bool yInLimits); TextRegion::FrameworkLineTests isRegionConcurrent(QPointF newPoint); TextRegion::FrameworkLineTests moveToPoint(QPointF newPoint); TextRegion::FrameworkLineTests addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph new_glyph); - void renderToTextFrame(PageItem* textNode, ParagraphStyle& pStyle); + void renderToTextFrame(PageItem* textNode); std::vector glyphs; bool isNew(); }; @@ -263,7 +263,7 @@ class TextFramework std::map addCharModes; TextRegion& activeTextRegion = TextRegion(); //faster than calling back on the vector all the time. - void addNewTextRegion(); + void addTextRegion(); void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); bool isNewLineOrRegion(QPointF newPosition); private: @@ -392,6 +392,7 @@ class SlaOutputDev : public OutputDev void beginTextObject(GfxState *state) override; void endTextObject(GfxState *state) override; void drawChar(GfxState *state, double /*x*/, double /*y*/, double /*dx*/, double /*dy*/, double /*originX*/, double /*originY*/, CharCode /*code*/, int /*nBytes*/, POPPLER_CONST_082 Unicode * /*u*/, int /*uLen*/) override; + void drawCharAsVector(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, POPPLER_CONST_082 Unicode* u, int uLen); GBool beginType3Char(GfxState * /*state*/, double /*x*/, double /*y*/, double /*dx*/, double /*dy*/, CharCode /*code*/, POPPLER_CONST_082 Unicode * /*u*/, int /*uLen*/) override; void endType3Char(GfxState * /*state*/) override; void type3D0(GfxState * /*state*/, double /*wx*/, double /*wy*/) override; From 34ba5eff3f53f60833afd39ea401cc557a23f64b Mon Sep 17 00:00:00 2001 From: olivetthered <37796246+olivetthered@users.noreply.github.com> Date: Thu, 11 Jun 2020 22:15:57 +0100 Subject: [PATCH 10/13] rename textFramework and tidyup AaddChar function poiinters rename textFramework and tidyup AaddChar function poiinters. one last pass at using function pointers as it id upto 20% faster and no slower than using switch --- scribus/plugins/import/pdf/slaoutput.cpp | 52 ++++++++++----------- scribus/plugins/import/pdf/slaoutput.h | 59 +++++++++--------------- 2 files changed, 47 insertions(+), 64 deletions(-) diff --git a/scribus/plugins/import/pdf/slaoutput.cpp b/scribus/plugins/import/pdf/slaoutput.cpp index a92e4bb79..f4f092cf0 100644 --- a/scribus/plugins/import/pdf/slaoutput.cpp +++ b/scribus/plugins/import/pdf/slaoutput.cpp @@ -3370,7 +3370,7 @@ void SlaOutputDev::drawChar(GfxState* state, double x, double y, double dx, doub return; if (textRenderingMode < 8) { - m_textFramework.addChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + m_textRecognition.addChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); } } } @@ -3443,27 +3443,27 @@ void SlaOutputDev::type3D1(GfxState *state, double wx, double wy, double llx, do void SlaOutputDev::beginTextObject(GfxState *state) { pushGroup(); - if (importTextAsVectors == false && !m_textFramework.activeTextRegion.textRegionLines.empty()) { - m_textFramework.addTextRegion(); + if (importTextAsVectors == false && !m_textRecognition.activeTextRegion.textRegionLines.empty()) { + m_textRecognition.addTextRegion(); } } void SlaOutputDev::endTextObject(GfxState *state) { - if (importTextAsVectors == false && !m_textFramework.activeTextRegion.textRegionLines.empty()) { + if (importTextAsVectors == false && !m_textRecognition.activeTextRegion.textRegionLines.empty()) { // Add the last glyph to the textregion - QPointF glyphXY = m_textFramework.activeTextRegion.lastXY; - m_textFramework.activeTextRegion.lastXY.setX(m_textFramework.activeTextRegion.lastXY.x() - m_textFramework.activeTextRegion.glyphs.back().dx); - if (m_textFramework.activeTextRegion.addGlyphAtPoint(glyphXY, m_textFramework.activeTextRegion.glyphs.back()) == TextRegion::FrameworkLineTests::FAIL) { + QPointF glyphXY = m_textRecognition.activeTextRegion.lastXY; + m_textRecognition.activeTextRegion.lastXY.setX(m_textRecognition.activeTextRegion.lastXY.x() - m_textRecognition.activeTextRegion.glyphs.back().dx); + if (m_textRecognition.activeTextRegion.addGlyphAtPoint(glyphXY, m_textRecognition.activeTextRegion.glyphs.back()) == TextRegion::FrameworkLineTests::FAIL) { qDebug("FIXME: Rogue glyph detected, this should never happen because the copuror should move before glyphs in new regions are added."); } renderTextFrame(); - } else if (importTextAsVectors == false && !m_textFramework.activeTextRegion.textRegionLines.empty()) { + } else if (importTextAsVectors == false && !m_textRecognition.activeTextRegion.textRegionLines.empty()) { qDebug("FIXME:Rogue textblock"); } - m_textFramework.setCharMode(TextFramework::AddCharMode::ADDFIRSTCHAR); + m_textRecognition.setCharMode(PdfTextRecognition::AddCharMode::ADDFIRSTCHAR); // qDebug() << "SlaOutputDev::endTextObject"; if (!m_clipTextPath.isEmpty()) { @@ -4275,18 +4275,18 @@ void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) void SlaOutputDev::updateTextPos(GfxState* state) { QPointF newPosition = QPointF(state->getCurX(), state->getCurY()); - TextRegion* activeTextRegion = &m_textFramework.activeTextRegion; + TextRegion* activeTextRegion = &m_textRecognition.activeTextRegion; if (activeTextRegion->isNew() ) { activeTextRegion->textRegioBasenOrigin = newPosition; - m_textFramework.setCharMode(TextFramework::AddCharMode::ADDFIRSTCHAR); + m_textRecognition.setCharMode(PdfTextRecognition::AddCharMode::ADDFIRSTCHAR); } else { // if we've will move to a new line or new text region then update the current text region with the last glyph, this ensures all textlines and textregions have terminating glyphs. - if (m_textFramework.isNewLineOrRegion(newPosition)) + if (m_textRecognition.isNewLineOrRegion(newPosition)) { QPointF glyphPosition = activeTextRegion->lastXY; activeTextRegion->lastXY.setX(activeTextRegion->lastXY.x() - activeTextRegion->glyphs.back().dx); @@ -4305,7 +4305,7 @@ void SlaOutputDev::updateTextPos(GfxState* state) renderTextFrame(); //Create and initilize a new TextRegion - m_textFramework.addTextRegion(); + m_textRecognition.addTextRegion(); updateTextPos(state); } } @@ -4315,7 +4315,7 @@ void SlaOutputDev::renderTextFrame() //TODO: Implement, this should all in based the framework //qDebug() << "_flushText() m_doc->currentPage()->xOffset():" << m_doc->currentPage()->xOffset(); // Ignore empty strings - auto activeTextRegion = &m_textFramework.activeTextRegion; + auto activeTextRegion = &m_textRecognition.activeTextRegion; if (activeTextRegion->glyphs.empty()) // We don't clear the glyphs any more or at least until the whole page has been rendered glyphs.clear(); return; @@ -4444,29 +4444,29 @@ void SlaOutputDev::finishItem(PageItem* item) //item->setLineTransparency(1.0); } -TextFramework::TextFramework() +PdfTextRecognition::PdfTextRecognition() { m_textRegions.push_back(activeTextRegion); setCharMode(AddCharMode::ADDFIRSTCHAR); } -TextFramework::~TextFramework() +PdfTextRecognition::~PdfTextRecognition() { } -void TextFramework::addTextRegion() +void PdfTextRecognition::addTextRegion() { activeTextRegion = TextRegion(); m_textRegions.push_back(activeTextRegion); - setCharMode(TextFramework::AddCharMode::ADDFIRSTCHAR); + setCharMode(PdfTextRecognition::AddCharMode::ADDFIRSTCHAR); } -void TextFramework::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +void PdfTextRecognition::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { (this->*(m_addChar))(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); } -bool TextFramework::isNewLineOrRegion(QPointF newPosition) +bool PdfTextRecognition::isNewLineOrRegion(QPointF newPosition) { return (activeTextRegion.collinear(activeTextRegion.lastXY.y(), activeTextRegion.textRegionLines.back().baseOrigin.y()) && !activeTextRegion.collinear(newPosition.y(), activeTextRegion.lastXY.y())) @@ -4474,10 +4474,10 @@ bool TextFramework::isNewLineOrRegion(QPointF newPosition) && !activeTextRegion.isCloseToX(newPosition.x(), activeTextRegion.lastXY.x())); } -PdfGlyph TextFramework::AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +PdfGlyph PdfTextRecognition::AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { //qDebug() << "AddFirstChar() '" << u << " : " << uLen; - PdfGlyph newGlyph = TextFramework::AddCharCommon(state, x, y, dx, dy, u, uLen); + PdfGlyph newGlyph = PdfTextRecognition::AddCharCommon(state, x, y, dx, dy, u, uLen); activeTextRegion.glyphs.push_back(newGlyph); setCharMode(AddCharMode::ADDBASICCHAR); @@ -4487,7 +4487,7 @@ PdfGlyph TextFramework::AddFirstChar(GfxState* state, double x, double y, double qDebug("FIXME: Rogue glyph detected, this should never happen because the coursor should move before glyphs in new regions are added."); return newGlyph; } -PdfGlyph TextFramework::AddCharCommon(GfxState* state, double x, double y, double dx, double dy, Unicode const* u, int uLen) +PdfGlyph PdfTextRecognition::AddCharCommon(GfxState* state, double x, double y, double dx, double dy, Unicode const* u, int uLen) { //qDebug() << "AddBasicChar() '" << u << " : " << uLen; PdfGlyph newGlyph; @@ -4504,7 +4504,7 @@ PdfGlyph TextFramework::AddCharCommon(GfxState* state, double x, double y, doubl return newGlyph; } -PdfGlyph TextFramework::AddBasicChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +PdfGlyph PdfTextRecognition::AddBasicChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { PdfGlyph newGlyph = AddCharCommon(state, x, y, dx, dy, u, uLen); activeTextRegion.lastXY = QPointF(x, y); @@ -4512,7 +4512,7 @@ PdfGlyph TextFramework::AddBasicChar(GfxState* state, double x, double y, double return newGlyph; } /*TODO: Currently not implemented, just stub code*/ -PdfGlyph TextFramework::AddCharWithNewStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +PdfGlyph PdfTextRecognition::AddCharWithNewStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { //qDebug() << "AddCharWithNewStyle() '" << u << " : " << uLen; auto newGlyph = AddCharCommon(state, x, y, dx, dy, u, uLen); @@ -4521,7 +4521,7 @@ PdfGlyph TextFramework::AddCharWithNewStyle(GfxState* state, double x, double y, } /*TODO: Currently not implemented, just stub code*/ -PdfGlyph TextFramework::AddCharWithPreviousStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +PdfGlyph PdfTextRecognition::AddCharWithPreviousStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { //qDebug() << "AddCharWithPreviousStyle() '" << u << " : " << uLen; auto newGlyph = AddCharCommon(state, x, y, dx, dy, u, uLen); diff --git a/scribus/plugins/import/pdf/slaoutput.h b/scribus/plugins/import/pdf/slaoutput.h index 39b91e276..e27e2042c 100644 --- a/scribus/plugins/import/pdf/slaoutput.h +++ b/scribus/plugins/import/pdf/slaoutput.h @@ -155,7 +155,7 @@ class AnoOutputDev : public OutputDev /* PDF TextBox Framework */ /* -* Holds all the dtails for each glyph in the text imported from the pdf file. +* Holds all the details for each glyph in the text imported from the pdf file. * */ struct PdfGlyph @@ -216,59 +216,42 @@ class TextRegion }; -class TextFramework +class PdfTextRecognition { public: - TextFramework(); - ~TextFramework(); + PdfTextRecognition(); + ~PdfTextRecognition(); enum class AddCharMode { - ADDFIRSTCHAR, - ADDBASICCHAR, - ADDCHARWITHNEWSTYLE, - ADDCHARWITHPREVIOUSSTYLE + ADDFIRSTCHAR = 0, + ADDBASICCHAR = 1, + ADDCHARWITHNEWSTYLE = 2, + ADDCHARWITHPREVIOUSSTYLE = 3 }; void setCharMode(AddCharMode mode) { - auto addCharModal = addCharModes[mode]; - if (!addCharModal) - { - addCharModal = nullptr; - switch (mode) - { - case AddCharMode::ADDFIRSTCHAR: - addCharModal = &TextFramework::AddFirstChar; - break; - case AddCharMode::ADDBASICCHAR: - addCharModal = &TextFramework::AddBasicChar; - break; - case AddCharMode::ADDCHARWITHNEWSTYLE: - addCharModal = &TextFramework::AddCharWithNewStyle; - break; - case AddCharMode::ADDCHARWITHPREVIOUSSTYLE: - addCharModal = &TextFramework::AddCharWithPreviousStyle; - break; - default: - // FIXME: This should never happen, set to a default - addCharModal = &TextFramework::AddBasicChar; - } - addCharModes[mode] = addCharModal; - } - m_addChar = addCharModal; + m_addChar = m_addCharModes[static_cast(mode)]; } - - - std::map addCharModes; TextRegion& activeTextRegion = TextRegion(); //faster than calling back on the vector all the time. void addTextRegion(); void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); bool isNewLineOrRegion(QPointF newPosition); private: - PdfGlyph(TextFramework::* m_addChar)(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) = {}; + std::vector m_textRegions = std::vector(); + + typedef PdfGlyph(PdfTextRecognition::* addCharFP)(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); + const addCharFP m_addCharModes[4] = + { + &PdfTextRecognition::AddFirstChar, + &PdfTextRecognition::AddBasicChar, + &PdfTextRecognition::AddCharWithNewStyle, + &PdfTextRecognition::AddCharWithPreviousStyle + }; + addCharFP m_addChar; // using function pointers over switch statements is roughly between 5% and 20% faster when tested with a standard load of other functionality PdfGlyph AddCharCommon(GfxState* state, double x, double y, double dx, double dy, Unicode const* u, int uLen); PdfGlyph AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); PdfGlyph AddBasicChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); @@ -504,7 +487,7 @@ class SlaOutputDev : public OutputDev QHash m_radioButtons; int m_actPage; //PDF Textbox framework - TextFramework m_textFramework = {}; + PdfTextRecognition m_textRecognition = {}; }; #endif From b3f57a4518d7b1f4a68e710f3c75f91157feb64e Mon Sep 17 00:00:00 2001 From: olivetthered <37796246+olivetthered@users.noreply.github.com> Date: Thu, 18 Jun 2020 19:26:14 +0100 Subject: [PATCH 11/13] replace addcghar function pointers with a awiutch statement --- scribus/plugins/import/pdf/slaoutput.cpp | 22 +++++++++++++++++++--- scribus/plugins/import/pdf/slaoutput.h | 15 +++------------ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/scribus/plugins/import/pdf/slaoutput.cpp b/scribus/plugins/import/pdf/slaoutput.cpp index f4f092cf0..7639d92ae 100644 --- a/scribus/plugins/import/pdf/slaoutput.cpp +++ b/scribus/plugins/import/pdf/slaoutput.cpp @@ -3370,7 +3370,7 @@ void SlaOutputDev::drawChar(GfxState* state, double x, double y, double dx, doub return; if (textRenderingMode < 8) { - m_textRecognition.addChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + m_textRecognition.addChar(state,x,y,dx,dy,originX, originY, code, nBytes, u, uLen) } } } @@ -4461,11 +4461,27 @@ void PdfTextRecognition::addTextRegion() setCharMode(PdfTextRecognition::AddCharMode::ADDFIRSTCHAR); } -void PdfTextRecognition::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +void PdfTextRecognition::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, POPPLER_CONST_082 Unicode* u, int uLen) { - (this->*(m_addChar))(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + + switch (this->m_addCharMode) + { + case AddCharMode::ADDFIRSTCHAR: + AddFirstChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + break; + case AddCharMode::ADDBASICCHAR: + AddBasicChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + break; + case AddCharMode::ADDCHARWITHNEWSTYLE: + AddCharWithNewStyle(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + break; + case AddCharMode::ADDCHARWITHPREVIOUSSTYLE: + AddCharWithPreviousStyle(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + break; + } } + bool PdfTextRecognition::isNewLineOrRegion(QPointF newPosition) { return (activeTextRegion.collinear(activeTextRegion.lastXY.y(), activeTextRegion.textRegionLines.back().baseOrigin.y()) && diff --git a/scribus/plugins/import/pdf/slaoutput.h b/scribus/plugins/import/pdf/slaoutput.h index e27e2042c..90ef9f3f6 100644 --- a/scribus/plugins/import/pdf/slaoutput.h +++ b/scribus/plugins/import/pdf/slaoutput.h @@ -232,26 +232,17 @@ class PdfTextRecognition void setCharMode(AddCharMode mode) { - m_addChar = m_addCharModes[static_cast(mode)]; + m_addCharMode = mode; } TextRegion& activeTextRegion = TextRegion(); //faster than calling back on the vector all the time. void addTextRegion(); - void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); + void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, POPPLER_CONST_082 Unicode* u, int uLen); bool isNewLineOrRegion(QPointF newPosition); private: std::vector m_textRegions = std::vector(); - - typedef PdfGlyph(PdfTextRecognition::* addCharFP)(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); - const addCharFP m_addCharModes[4] = - { - &PdfTextRecognition::AddFirstChar, - &PdfTextRecognition::AddBasicChar, - &PdfTextRecognition::AddCharWithNewStyle, - &PdfTextRecognition::AddCharWithPreviousStyle - }; - addCharFP m_addChar; // using function pointers over switch statements is roughly between 5% and 20% faster when tested with a standard load of other functionality + AddCharMode m_addCharMode = AddCharMode::ADDFIRSTCHAR; PdfGlyph AddCharCommon(GfxState* state, double x, double y, double dx, double dy, Unicode const* u, int uLen); PdfGlyph AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); PdfGlyph AddBasicChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); From 05d438647f3e2b81ead7e7e24f576ba261852d95 Mon Sep 17 00:00:00 2001 From: olivetthered <37796246+olivetthered@users.noreply.github.com> Date: Sun, 21 Jun 2020 04:28:09 +0100 Subject: [PATCH 12/13] comments, tidyups and seperate pdftextrecognition has regersion in matching newline comments, tidyups and seperate pdftextrecognition has regersion in matching newline that was probably introduced when I implemented ale, if else simplification without properly checking it first. --- scribus/plugins/import/pdf/CMakeLists.txt | 1 + .../plugins/import/pdf/pdftextrecognition.cpp | 388 +++++++++++++ .../plugins/import/pdf/pdftextrecognition.h | 135 +++++ scribus/plugins/import/pdf/slaoutput.cpp | 523 +++--------------- scribus/plugins/import/pdf/slaoutput.h | 100 +--- 5 files changed, 589 insertions(+), 558 deletions(-) create mode 100644 scribus/plugins/import/pdf/pdftextrecognition.cpp create mode 100644 scribus/plugins/import/pdf/pdftextrecognition.h diff --git a/scribus/plugins/import/pdf/CMakeLists.txt b/scribus/plugins/import/pdf/CMakeLists.txt index 85760d96e..1ee6cf74e 100644 --- a/scribus/plugins/import/pdf/CMakeLists.txt +++ b/scribus/plugins/import/pdf/CMakeLists.txt @@ -20,6 +20,7 @@ set(IMPORTPDF_PLUGIN_SOURCES importpdfplugin.cpp pdfimportoptions.cpp slaoutput.cpp + pdftextrecognition.cpp ) if(HAVE_POPPLER) diff --git a/scribus/plugins/import/pdf/pdftextrecognition.cpp b/scribus/plugins/import/pdf/pdftextrecognition.cpp new file mode 100644 index 000000000..4bfd02c3d --- /dev/null +++ b/scribus/plugins/import/pdf/pdftextrecognition.cpp @@ -0,0 +1,388 @@ +/* +For general Scribus (>=1.3.2) copyright and licensing information please refer +to the COPYING file provided with the program. Following this notice may exist +a copyright and/or license notice that predates the release of Scribus 1.3.2 +for which a new license (GPL+exception) is in place. +*/ + +#include "pdftextrecognition.h" + +/* +* constructor, initialize the textRegions vector and set the addChar mode +*/ +PdfTextRecognition::PdfTextRecognition() +{ + m_textRegions.push_back(activeTextRegion); + setCharMode(AddCharMode::ADDFIRSTCHAR); +} + +/* +* nothing to do in the destructor yet +*/ +PdfTextRecognition::~PdfTextRecognition() +{ +} + +/* +* add a new text region and make it the active region +*/ +void PdfTextRecognition::addTextRegion() +{ + activeTextRegion = TextRegion(); + m_textRegions.push_back(activeTextRegion); + setCharMode(PdfTextRecognition::AddCharMode::ADDFIRSTCHAR); +} + +/* +* function called via integration with poppler's addChar callback. It decides how to add the charter based on the mode that is set +*/ +void PdfTextRecognition::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, POPPLER_CONST_082 Unicode* u, int uLen) +{ + + switch (this->m_addCharMode) + { + case AddCharMode::ADDFIRSTCHAR: + AddFirstChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + break; + case AddCharMode::ADDBASICCHAR: + AddBasicChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + break; + case AddCharMode::ADDCHARWITHNEWSTYLE: + AddCharWithNewStyle(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + break; + case AddCharMode::ADDCHARWITHPREVIOUSSTYLE: + AddCharWithPreviousStyle(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); + break; + } +} + +/* +* basic test to see if the point lies in a new line or region +*/ +bool PdfTextRecognition::isNewLineOrRegion(QPointF newPosition) +{ + return (activeTextRegion.collinear(activeTextRegion.lastXY.y(), activeTextRegion.textRegionLines.back().baseOrigin.y()) && + !activeTextRegion.collinear(newPosition.y(), activeTextRegion.lastXY.y())) + || (activeTextRegion.collinear(newPosition.y(), activeTextRegion.lastXY.y()) + && !activeTextRegion.isCloseToX(newPosition.x(), activeTextRegion.lastXY.x())); +} + + +/* +* basic functionality to be performed when addChar is called +* FIXME: what to do when uLen != 1 +*/ +PdfGlyph PdfTextRecognition::AddCharCommon(GfxState* state, double x, double y, double dx, double dy, Unicode const* u, int uLen) +{ + //qDebug() << "AddBasicChar() '" << u << " : " << uLen; + PdfGlyph newGlyph; + newGlyph.dx = dx; + newGlyph.dy = dy; + + // Convert the character to UTF-16 since that's our SVG document's encoding + if (uLen > 1) + qDebug() << "AddBasicChar() '" << u << " : " << uLen; + newGlyph.code = static_cast(u[uLen - 1]); + newGlyph.rise = state->getRise(); + return newGlyph; +} + +/* +* Tell the text region to add a glyph so that line segments and regions be created +* If the character being added is the first character in a textregion or after a change in positioning or styles or the end of a line +*/ +PdfGlyph PdfTextRecognition::AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +{ + //qDebug() << "AddFirstChar() '" << u << " : " << uLen; + PdfGlyph newGlyph = PdfTextRecognition::AddCharCommon(state, x, y, dx, dy, u, uLen); + activeTextRegion.glyphs.push_back(newGlyph); + setCharMode(AddCharMode::ADDBASICCHAR); + + //only need to be called for the very first point + auto success = activeTextRegion.addGlyphAtPoint(QPointF(x, y), newGlyph); + if (success == TextRegion::LineType::FAIL) + qDebug("FIXME: Rogue glyph detected, this should never happen because the cursor should move before glyphs in new regions are added."); + return newGlyph; +} + +/* +* just add a character to the textregion without doing anything special +*/ +PdfGlyph PdfTextRecognition::AddBasicChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +{ + PdfGlyph newGlyph = AddCharCommon(state, x, y, dx, dy, u, uLen); + activeTextRegion.lastXY = QPointF(x, y); + activeTextRegion.glyphs.push_back(newGlyph); + return newGlyph; +} + +/* +* Apply a new style to this glyph ands glyphs that follow and add it to the style stack +* TODO: Currently not implemented, just stub code +*/ +PdfGlyph PdfTextRecognition::AddCharWithNewStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +{ + //qDebug() << "AddCharWithNewStyle() '" << u << " : " << uLen; + auto newGlyph = AddCharCommon(state, x, y, dx, dy, u, uLen); + activeTextRegion.glyphs.push_back(newGlyph); + return newGlyph; +} + +/* +* return to the previous style on the style stack +* TODO: Currently not implemented, just stub code +*/ +PdfGlyph PdfTextRecognition::AddCharWithPreviousStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) +{ + //qDebug() << "AddCharWithPreviousStyle() '" << u << " : " << uLen; + auto newGlyph = AddCharCommon(state, x, y, dx, dy, u, uLen); + activeTextRegion.glyphs.push_back(newGlyph); + return newGlyph; +} + +/* +* functions to do fuzzy testing on the proximity of points to one another and in relation to the textregion +* FIXME: There should be a parameter in the UI to set the matching tolerance but hard code for now +*/ + +/* +* In geometry, collinearity of a set of points is the property of their lying on a single line. A set of points with this property is said to be collinear. +* In greater generality, the term has been used for aligned objects, that is, things being "in a line" or "in a row". +* PDF never deviates from the line when it comes to collinear, but allow for 1pixel of divergence +*/ +bool TextRegion::collinear(qreal a, qreal b) +{ + return abs(a - b) < 1 ? true : false; +} + +/* +* like collinear but we allow a deviation of 6 text widths from between positions or 1 text width from the textregion's x origin +*/ +bool TextRegion::isCloseToX(qreal x1, qreal x2) +{ + //FIXME: This should use the char width not linespacing which is y + return (abs(x2 - x1) <= lineSpacing * 6) || (abs(x1 - this->textRegioBasenOrigin.x()) <= lineSpacing); +} + +/* +* like collinear but we allow a deviation of 3 text heights downwards but none upwards +*/ +bool TextRegion::isCloseToY(qreal y1, qreal y2) +{ + + return (y2 - y1) >= 0 && y2 - y1 <= lineSpacing * 3; +} + +/* +* less than, page upwards, the last y value but bot more than the line spacing less, could also use the base line of the last line to be more accurate +*/ +bool TextRegion::adjunctLesser(qreal testY, qreal lastY, qreal baseY) +{ + return (testY > lastY + && testY <= baseY + lineSpacing + && lastY <= baseY + lineSpacing); +} + +/* +* greater, page downwards, than the last y value but not more than 3/4 of a line space below baseline +*/ +bool TextRegion::adjunctGreater(qreal testY, qreal lastY, qreal baseY) +{ + return (testY <= lastY + && testY >= baseY - lineSpacing * 0.75 + && lastY != baseY); +} + +/* +* Test to see if the point is part of the current block of text or is part of a new block of text(FrameworkLineTests::FAIL). +* checks to see if it's the first point, on the same line, super and sub script, returning to baseline from super script or if we are on a new line. +* matching is fuzzy allowing for multiple linespaces and text indentation. right hand justifications still needs to be dealt with as well as identifying if we are on a new paragraph +* tests are weaker if we are on the first and moving to the second lines of text because we don't have enough information about how the text in the region +* is formatted and in those cases the linespace is taken to be twice the glyph width. +* FIXME: This needs fixing when font support is added and the ascending and descending values for the font should be used instead of the glyphs width. +* TODO: support LineType::STYLESUBSCRIPT +*/ +TextRegion::LineType TextRegion::linearTest(QPointF point, bool xInLimits, bool yInLimits) +{ + if (collinear(point.y(), lastXY.y())) + if (collinear(point.x(), lastXY.x())) + return LineType::FIRSTPOINT; + else if (xInLimits) + return LineType::SAMELINE; + else if (adjunctLesser(point.y(), lastXY.y(), lineBaseXY.y())) + return LineType::STYLESUPERSCRIPT; + else if (adjunctGreater(point.y(), lastXY.y(), lineBaseXY.y())) + if (collinear(point.y(), lineBaseXY.y())) + return LineType::STYLENORMALRETURN; + else + return LineType::STYLESUPERSCRIPT; + else if(isCloseToX(point.x(), textRegioBasenOrigin.x()) && isCloseToY(point.y(), lastXY.y())) + if (textRegionLines.size() >= 2) + return LineType::NEWLINE; + else if (textRegionLines.size() == 1) + return LineType::NEWLINE; + qDebug() << "NEWLINE2 oops:" << point << ":" << textRegioBasenOrigin << ":" << lineSpacing; + return LineType::FAIL; +} + +/* +* Perform some fuzzy checks to see if newPoint can reasonably be ascribed to the current textframe. +* FIXME: It may be that move and addGlyph need different versions of isCloseToX and isCloseToY but keep them the same just for now +*/ +TextRegion::LineType TextRegion::isRegionConcurrent(QPointF newPoint) +{ + if (glyphs.empty()) + { + lineBaseXY = newPoint; + lastXY = newPoint; + } + + bool xInLimits = isCloseToX(newPoint.x(), lastXY.x()); + bool yInLimits = isCloseToY(newPoint.y(), lastXY.y()); + LineType pass = linearTest(newPoint, xInLimits, yInLimits); + return pass; +} + +/* +* Move the position of the cursor to a new point, +* test if that point is within the current textframe or within a new textframe. +* initialize the textregion and setup lines and segments +* TODO: iscloseto x and y may need to be different from addGlyph but use thge common isRegionbConcurrent for now +* need to check to see if we are creating a new paragraph or not. +* basically if the cursor is returned to x origin before it reached x width. +* Also needs to have support for rotated text, but I expect I'll add this by removing the text rotation +* from calls to movepoint and addGlyph and instead rotating the whole text region as a block +*/ +TextRegion::LineType TextRegion::moveToPoint(QPointF newPoint) +{ + //qDebug() << "moveToPoint: " << newPoint; + + if (glyphs.empty()) + { + lineBaseXY = newPoint; + lastXY = newPoint; + } + LineType mode = isRegionConcurrent(newPoint); + if (mode == LineType::FAIL) + return mode; + + TextRegionLine* textRegionLine = nullptr; + if (mode == LineType::NEWLINE || mode == LineType::FIRSTPOINT) + { + if (mode != LineType::FIRSTPOINT || textRegionLines.empty()) + textRegionLines.push_back(TextRegionLine()); + + textRegionLine = &textRegionLines.back(); + textRegionLine->baseOrigin = newPoint; + if (mode == LineType::NEWLINE) + { + textRegionLine->maxHeight = abs(newPoint.y() - lastXY.y()); + if (textRegionLines.size() == 2) + lineSpacing = abs(newPoint.y() - lastXY.y()) + 1; + } + } + + textRegionLine = &textRegionLines.back(); + if ((mode == LineType::FIRSTPOINT && textRegionLine->segments.empty()) || mode == LineType::NEWLINE + || mode != LineType::FIRSTPOINT && textRegionLine->segments[0].glyphIndex != textRegionLine->glyphIndex) + { + TextRegionLine newSegment = TextRegionLine(); + textRegionLine->segments.push_back(newSegment); + } + TextRegionLine* segment = &textRegionLine->segments.back(); + segment->baseOrigin = newPoint; + segment->maxHeight = (mode == LineType::STYLESUPERSCRIPT) ? + abs(lineSpacing - (newPoint.y() - lastXY.y())) : + textRegionLines.back().maxHeight; + + if (mode != LineType::NEWLINE && mode != LineType::FIRSTPOINT) + { + textRegionLines.back().segments.back().width = abs(textRegionLines.back().segments.back().baseOrigin.x() - newPoint.x()); + textRegionLine = &textRegionLines.back(); + textRegionLine->width = abs(textRegionLine->baseOrigin.x() - newPoint.x()); + } + + maxHeight = abs(textRegioBasenOrigin.y() - newPoint.y()) > maxHeight ? abs(textRegioBasenOrigin.y() - newPoint.y()) : maxHeight; + lastXY = newPoint; + + return mode; +} + +/* +* Add a new glyph to the current line segment, lines and segments should already have been setup by the +* moveto function which should generally be called prior to addGlyph to setup the lines and segments correctly. +* does some basic calculations to determine and save withs and heights and linespacings of texts etc... +* FIXME: these need to be changed to use the mode average of all glyps added to the text frame instead of just picking the first ones we come accross +* the mode average can also be used to determine the base font style when fonts are added +* left and right hand margins however need to use the maximum and minimum, support for right hand justification +* and centered text needs to be added as we only support left and fully justified at the moment. +* Approximated heights and widths and linespaces need to use the correct font data when font support has been added, +* but for now just use the x advance value. using font data should also allow for the support of rotated text that may use a mixture of x and y advance +*/ +TextRegion::LineType TextRegion::addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph newGlyph) +{ + QPointF movedGlyphPoint = QPointF(newGlyphPoint.x() + newGlyph.dx, newGlyphPoint.y() + newGlyph.dy); + if (glyphs.size() == 1) + { + lineSpacing = newGlyph.dx * 2; + lastXY = newGlyphPoint; + lineBaseXY = newGlyphPoint; + } + LineType mode = isRegionConcurrent(newGlyphPoint); + if (mode == LineType::FAIL) + return mode; + + maxHeight = abs(textRegioBasenOrigin.y() - movedGlyphPoint.y()) + lineSpacing > maxHeight ? abs(textRegioBasenOrigin.y() - movedGlyphPoint.y()) + lineSpacing : maxHeight; + + TextRegionLine* textRegionLine = &textRegionLines.back(); + if (mode == LineType::NEWLINE || mode == LineType::FIRSTPOINT) + { + textRegionLine->glyphIndex = glyphs.size() - 1; + textRegionLine->baseOrigin = QPointF(textRegioBasenOrigin.x(), newGlyphPoint.y()); + } + + TextRegionLine* segment = &textRegionLine->segments.back(); + segment->width = abs(movedGlyphPoint.x() - segment->baseOrigin.x()); + segment->glyphIndex = glyphs.size() - 1; + qreal thisHeight = textRegionLines.size() > 1 ? + abs(newGlyphPoint.y() - textRegionLines[textRegionLines.size() - 2].baseOrigin.y()) : + newGlyph.dx; + + segment->maxHeight = thisHeight > segment->maxHeight ? thisHeight : segment->maxHeight; + textRegionLine->maxHeight = textRegionLine->maxHeight > thisHeight ? textRegionLine->maxHeight : thisHeight; + textRegionLine->width = abs(movedGlyphPoint.x() - textRegionLine->baseOrigin.x()); + + maxWidth = textRegionLine->width > maxWidth ? textRegionLine->width : maxWidth; + if (textRegionLine->segments.size() == 1) + lineBaseXY = textRegionLine->baseOrigin; + + lastXY = movedGlyphPoint; + + return mode; +} + +/* +* Render the text region to the frame, +* nothing clever for now, just apply the whole block of text to the textNode +* TODO: Add support for fonts and styles based on line segments +* add support for rotated text +*/ +void TextRegion::renderToTextFrame(PageItem* textNode) +{ + textNode->setWidthHeight(this->maxWidth, this->maxHeight); + QString bodyText = ""; + for (int glyphIndex = this->textRegionLines.begin()->glyphIndex; glyphIndex <= this->textRegionLines.back().segments.back().glyphIndex; glyphIndex++) + bodyText += glyphs[glyphIndex].code; + + textNode->itemText.insertChars(bodyText); + textNode->frameTextEnd(); +} + +/* +* Quick test to see if this is a virgin textregion +*/ +bool TextRegion::isNew() +{ + return textRegionLines.empty() || + glyphs.empty(); +} diff --git a/scribus/plugins/import/pdf/pdftextrecognition.h b/scribus/plugins/import/pdf/pdftextrecognition.h new file mode 100644 index 000000000..78f8ffde2 --- /dev/null +++ b/scribus/plugins/import/pdf/pdftextrecognition.h @@ -0,0 +1,135 @@ +/* +For general Scribus (>=1.3.2) copyright and licensing information please refer +to the COPYING file provided with the program. Following this notice may exist +a copyright and/or license notice that predates the release of Scribus 1.3.2 +for which a new license (GPL+exception) is in place. +*/ +#ifndef PDFTEXTRECOGNITION_H +#define PDFTEXTRECOGNITION_H + +#include +#include +#include + +#include "pageitem.h" +#include "importpdfconfig.h" + +#include +#include + +/* PDF TextBox Framework */ +/* +* Holds all the details for each glyph in the text imported from the pdf file. +* +*/ +struct PdfGlyph +{ + double dx; // X advance value + double dy; // Y advance value + double rise; // Text rise parameter + QChar code; // UTF-16 coded character +}; + + +class TextRegionLine +{ +public: + qreal maxHeight = {}; + //we can probably use maxHeight for this. + qreal width = {}; + int glyphIndex = {}; + QPointF baseOrigin = QPointF({}, {}); + std::vector segments = std::vector(); + +}; + +class TextRegion +{ +public: + enum class LineType + { + FIRSTPOINT, + SAMELINE, + STYLESUPERSCRIPT, + STYLENORMALRETURN, + STYLEBELOWBASELINE, + NEWLINE, + ENDOFLINE, //TODO: Implement an end of line test + FAIL + }; +# + /* +* the bounding box shape splines in percentage of width and height. In this case 100% as we want to clip shape to be the full TextBox width and height. */ + static constexpr double boundingBoxShape[32] = { 0.0,0.0 + ,0.0,0.0 + ,100.0,0.0 + ,100.0,0.0 + ,100.0,0.0 + ,100.0,0.0 + ,100.0,100.0 + ,100.0,100.0 + ,100.0,100.0 + ,100.0,100.0 + ,0.0,100.0 + ,0.0,100.0 + ,0.0,100.0 + ,0.0,100.0 + ,0.0,0.0 + ,0.0,0.0 + }; + + QPointF textRegioBasenOrigin = QPointF({}, {}); + qreal maxHeight = {}; + qreal lineSpacing = { 1 }; + std::vector textRegionLines = std::vector(); + qreal maxWidth = {}; + QPointF lineBaseXY = QPointF({ }, { }); //updated with the best match left value from all the textRegionLines and the best bottom value from the textRegionLines.segments; + QPointF lastXY = QPointF({}, {}); + static bool collinear(qreal a, qreal b); + bool isCloseToX(qreal x1, qreal x2); + bool isCloseToY(qreal y1, qreal y2); + bool adjunctLesser(qreal testY, qreal lastY, qreal baseY); + bool adjunctGreater(qreal testY, qreal lastY, qreal baseY); + TextRegion::LineType linearTest(QPointF point, bool xInLimits, bool yInLimits); + TextRegion::LineType isRegionConcurrent(QPointF newPoint); + TextRegion::LineType moveToPoint(QPointF newPoint); + TextRegion::LineType addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph new_glyph); + void renderToTextFrame(PageItem* textNode); + std::vector glyphs; + bool isNew(); +}; + +class PdfTextRecognition +{ +public: + PdfTextRecognition(); + ~PdfTextRecognition(); + + enum class AddCharMode + { + ADDFIRSTCHAR, + ADDBASICCHAR, + ADDCHARWITHNEWSTYLE, + ADDCHARWITHPREVIOUSSTYLE, + ADDCHARWITHBASESTLYE + }; + + void setCharMode(AddCharMode mode) + { + m_addCharMode = mode; + } + + TextRegion&& activeTextRegion = TextRegion(); //faster and cleaner than calling back on the vector all the time. + void addTextRegion(); + void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, POPPLER_CONST_082 Unicode* u, int uLen); + bool isNewLineOrRegion(QPointF newPosition); +private: + std::vector m_textRegions = std::vector(); + AddCharMode m_addCharMode = AddCharMode::ADDFIRSTCHAR; + PdfGlyph AddCharCommon(GfxState* state, double x, double y, double dx, double dy, Unicode const* u, int uLen); + PdfGlyph AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); + PdfGlyph AddBasicChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); + PdfGlyph AddCharWithNewStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); + PdfGlyph AddCharWithPreviousStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); +}; +#endif diff --git a/scribus/plugins/import/pdf/slaoutput.cpp b/scribus/plugins/import/pdf/slaoutput.cpp index 7639d92ae..ef850054c 100644 --- a/scribus/plugins/import/pdf/slaoutput.cpp +++ b/scribus/plugins/import/pdf/slaoutput.cpp @@ -3370,7 +3370,7 @@ void SlaOutputDev::drawChar(GfxState* state, double x, double y, double dx, doub return; if (textRenderingMode < 8) { - m_textRecognition.addChar(state,x,y,dx,dy,originX, originY, code, nBytes, u, uLen) + m_textRecognition.addChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); } } } @@ -3447,7 +3447,9 @@ void SlaOutputDev::beginTextObject(GfxState *state) m_textRecognition.addTextRegion(); } } - +/* + * NOTE: If a rogue glyph is detected it means that PdfTextRecognition &co. has a bug between the moveTo and addGlyphAtPoint function calls. in theory addGlyphAtPoint should never fail. +*/ void SlaOutputDev::endTextObject(GfxState *state) { @@ -3455,8 +3457,8 @@ void SlaOutputDev::endTextObject(GfxState *state) // Add the last glyph to the textregion QPointF glyphXY = m_textRecognition.activeTextRegion.lastXY; m_textRecognition.activeTextRegion.lastXY.setX(m_textRecognition.activeTextRegion.lastXY.x() - m_textRecognition.activeTextRegion.glyphs.back().dx); - if (m_textRecognition.activeTextRegion.addGlyphAtPoint(glyphXY, m_textRecognition.activeTextRegion.glyphs.back()) == TextRegion::FrameworkLineTests::FAIL) { - qDebug("FIXME: Rogue glyph detected, this should never happen because the copuror should move before glyphs in new regions are added."); + if (m_textRecognition.activeTextRegion.addGlyphAtPoint(glyphXY, m_textRecognition.activeTextRegion.glyphs.back()) == TextRegion::LineType::FAIL) { + qDebug("FIXME: Rogue glyph detected, this should never happen because the cursor should move before glyphs in new regions are added."); } renderTextFrame(); } else if (importTextAsVectors == false && !m_textRecognition.activeTextRegion.textRegionLines.empty()) { @@ -3486,7 +3488,7 @@ void SlaOutputDev::endTextObject(GfxState *state) ite = m_doc->groupObjectsSelection(tmpSel); else ite = gElements.Items.first(); - ite->setGroupClipping(false); + ite->setGroupClipping(false); ite->setFillTransparency(1.0 - state->getFillOpacity()); ite->setFillBlendmode(getBlendMode(state)); for (int as = 0; as < tmpSel->count(); ++as) @@ -3929,284 +3931,6 @@ bool SlaOutputDev::checkClip() } -/* -In geometry, collinearity of a set of points is the property of their lying on a single line. A set of points with this property is said to be collinear. -In greater generality, the term has been used for aligned objects, that is, things being "in a line" or "in a row". - PDF never deviates from the line when it comes to collinear, but allow for 1pixel of divergence -*/ -bool TextRegion::collinear(qreal a, qreal b) -{ - return abs(a - b) < 1 ? true : false; -} - -// like collinear but we allow a deviation of up to +-2 rejoin font text widths -bool TextRegion::isCloseToX(qreal x1, qreal x2) -{ - //FIXME: This should use the char width not linespacing which is y - return (abs(x2 - x1) <= lineSpacing * 6) || (abs(x1 - this->textRegioBasenOrigin.x()) <= lineSpacing); -} - -// like collinear but we allow a deviation of up to 3 rejoin font linespaces, but in one direction and half a line space in the other direction -bool TextRegion::isCloseToY(qreal y1, qreal y2) -{ - //FIXME: Actually test the correct magnitudes not the abs value. There should be a parameter in the ui to set the matching tolerance but hard code to allow 3 linespaces difference before we say that the y is out of scope. - return (y2 - y1) >= 0 && y2 - y1 <= lineSpacing * 3; -} - -// less than the last y value but bot more than the line spacing less, could also use the base line of the last line to be more accurate -bool TextRegion::adjunctLesser(qreal testY, qreal lastY, qreal baseY) -{ - return (testY > lastY - && testY <= baseY + lineSpacing - && lastY <= baseY + lineSpacing); -} - -// less than the last y value but bot more than the line spacing less, could also use the base line of the last line to be more accurate -bool TextRegion::adjunctGreater(qreal testY, qreal lastY, qreal baseY) -{ - return (testY <= lastY - && testY >= baseY - lineSpacing * 0.75 - && lastY != baseY); -} - -TextRegion::FrameworkLineTests TextRegion::linearTest(QPointF point, bool xInLimits, bool yInLimits) -{ - //TODO: add a FIRSTPOINT result as well - FrameworkLineTests pass = FrameworkLineTests::FAIL; - - //FIXME: I think this should be using baseXY not lastXY - if (collinear(point.y(), lastXY.y())) - { - if (collinear(point.x(), lastXY.x())) - { - pass = FrameworkLineTests::FIRSTPOINT; -#ifdef DEBUG_TEXT_IMPORT - qDebug() << "FIRSTPOINT"; -#endif - } // see if we are continuing along a line or if we can add a new line to take into account this first line may have truncated early, leaving the rest of the lines dangling out x's - else if (xInLimits) - { - // this is for item ##16 - // OK, this should only happen when a new glyph is added not when the cursor position is set, but in both cases we can call extend by the point and set the glyph to the current glyph checking that it's not a duplicate - //TODO: textRegionLines.end().extend(point).setGlyph(newGlyph); - pass = FrameworkLineTests::SAMELINE; -#ifdef DEBUG_TEXT_IMPORT - qDebug() << "SAMELINE " << point << " lastxy:"<< lastXY; -#endif - } - } // else see if y is a bit too much off the lastyx line to be linear - else if (adjunctLesser(point.y(), lastXY.y(), lineBaseXY.y())) - { - //TODO: character has gone superscript - pass = FrameworkLineTests::STYLESUPERSCRIPT; -#ifdef DEBUG_TEXT_IMPORT - qDebug() << "STYLESUPERSCRIPT point:" << point << " lastXY:" << lastXY << " lineBaseXY:" << lineBaseXY; -#endif - } - else if (adjunctGreater(point.y(),lastXY.y(),lineBaseXY.y())) - { - if (collinear(point.y(), lineBaseXY.y())) //PDF never deviates from the line when it comes to collinear - { - // were back on track - pass = FrameworkLineTests::STYLENORMALRETURN; -#ifdef DEBUG_TEXT_IMPORT - qDebug() << "STYLENORMALRETURN"; -#endif - } - else - { - //TODO: this character has overflowed the height, or is still superscript just not so much - pass = FrameworkLineTests::STYLESUPERSCRIPT; //could be STYLEBELOWBASELINE -#ifdef DEBUG_TEXT_IMPORT - qDebug() << "STYLESUBSCRIPT point:" << point << " lastXY:" << lastXY << " lineBaseXY:" << lineBaseXY; -#endif - //qDebug() << "STYLESUBSCRIPT: "; - } - } - else { - //TODO: We need to calculate things like new paragraphs and left hand justification - if (isCloseToX(point.x(), textRegioBasenOrigin.x())) - { - if (isCloseToY(point.y(), lastXY.y()) && !collinear(point.y(), lastXY.y())) - { - //TODO: We need to calculate things like new paragraphs and left hand justification - if ((textRegionLines.size() >= 2)) //TODO: Need to setup some parameters relating to width matching, they mainly relate to justification && closeToX(textRegionLines[textRegionLines.size() - 2].baseOrigin.x() + textRegionLines[textRegionLines.size() - 2].width, maxWidth)) - { - //TODO: add a new line and update the deltas - pass = FrameworkLineTests::NEWLINE; -#ifdef DEBUG_TEXT_IMPORT - qDebug() << "NEWLINE1 point:" << point << " _lastXY:" << lastXY << " origin: " << textRegioBasenOrigin << " this:" << this << " linespacing: " << lineSpacing; - -#endif // DEBUG - } // we only have the first line so far, so pass without much of a test. - else if (textRegionLines.size() == 1) - { - pass = FrameworkLineTests::NEWLINE; -#ifdef DEBUG_TEXT_IMPORT - qDebug() << "NEWLINE2 point:" << point << " _lastXY:" << lastXY << " origin: " << textRegioBasenOrigin << " this:" << this << " linespacing: " << lineSpacing; -#endif - } - } - } - else - { -#ifdef DEBUG_TEXT_IMPORT - qDebug() << "NEWLINE2 oops:"<baseOrigin = newPoint; - if (pass == FrameworkLineTests::NEWLINE) - { - textRegionLine->maxHeight = abs(newPoint.y() - lastXY.y()); - if (textRegionLines.size() == 2) - lineSpacing = abs(newPoint.y() - lastXY.y()) + 1; - //qDebug() << "setting lineSpacing to:" << lineSpacing; - } - } - - textRegionLine = &textRegionLines.back(); - if ((pass == FrameworkLineTests::FIRSTPOINT && textRegionLine->segments.empty()) || pass == FrameworkLineTests::NEWLINE || pass != FrameworkLineTests::FIRSTPOINT && textRegionLine->segments[0].glyphIndex != textRegionLine->glyphIndex) - { - TextRegionLine newSegment = TextRegionLine(); - textRegionLine->segments.push_back(newSegment); - } - TextRegionLine* segment = &textRegionLine->segments.back(); - segment->baseOrigin = newPoint; - segment->maxHeight = (pass == FrameworkLineTests::STYLESUPERSCRIPT) ? - abs(lineSpacing - (newPoint.y() - lastXY.y())) : - textRegionLines.back().maxHeight; - - if (pass != FrameworkLineTests::NEWLINE && pass != FrameworkLineTests::FIRSTPOINT) - { - textRegionLines.back().segments.back().width = abs(textRegionLines.back().segments.back().baseOrigin.x() - newPoint.x()); - textRegionLine = &textRegionLines.back(); - textRegionLine->width = abs(textRegionLine->baseOrigin.x() - newPoint.x()); - } - - maxHeight = abs(textRegioBasenOrigin.y() - newPoint.y()) > maxHeight ? abs(textRegioBasenOrigin.y() - newPoint.y()) : maxHeight; - lastXY = newPoint; - - return pass; -} - -//TODO:, extract some font heights instead of using dx all the time -TextRegion::FrameworkLineTests TextRegion::addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph newGlyph) -{ - QPointF movedGlyphPoint = QPointF(newGlyphPoint.x() + newGlyph.dx, newGlyphPoint.y() + newGlyph.dy); - //qDebug() << "addGlyphAtPoint start" << newGlyphPoint << " glyph:"<< new_glyph.code; - //FIXME: There should be no need for testing the scope when adding a new glyph because move to should have been called first but leave it in for now to catch any errors in the logic - if (glyphs.size() == 1) - { - // FIXME: do a propper lookup of the height - lineSpacing = newGlyph.dx * 2; - //qDebug() << "addGlyphAtPoint start"; - lastXY = newGlyphPoint; - lineBaseXY = newGlyphPoint; - } - // TODO: add and move may want different versions of isCloseToX, but for now use the same generic function for both - FrameworkLineTests pass = isRegionConcurrent(newGlyphPoint); - if (pass == FrameworkLineTests::FAIL) - return pass; - - maxHeight = abs(textRegioBasenOrigin.y() - movedGlyphPoint.y()) + lineSpacing > maxHeight ? abs(textRegioBasenOrigin.y() - movedGlyphPoint.y()) + lineSpacing : maxHeight; - //move to deals with setting newlines and segments, all we have to do is populate them with the parameters the glyph gives us such as it's width and height and set the glyph index for the newlines and segments - - TextRegionLine* textRegionLine = &textRegionLines.back(); - if (pass == FrameworkLineTests::NEWLINE || pass == FrameworkLineTests::FIRSTPOINT) { - textRegionLine->glyphIndex = glyphs.size() - 1; - textRegionLine->baseOrigin = QPointF(textRegioBasenOrigin.x(), newGlyphPoint.y()); - } - - TextRegionLine *segment = &textRegionLine->segments.back(); - segment->width = abs(movedGlyphPoint.x() - segment->baseOrigin.x()); - segment->glyphIndex = glyphs.size() - 1; - qreal thisHeight = textRegionLines.size() > 1 ? - abs(newGlyphPoint.y() - textRegionLines[textRegionLines.size() - 2].baseOrigin.y()) : - newGlyph.dx; - - segment->maxHeight = thisHeight > segment->maxHeight ? thisHeight : segment->maxHeight; - textRegionLine->maxHeight = textRegionLine->maxHeight > thisHeight ? textRegionLine->maxHeight : thisHeight; - textRegionLine->width = abs(movedGlyphPoint.x() - textRegionLine->baseOrigin.x()); - - maxWidth = textRegionLine->width > maxWidth ? textRegionLine->width : maxWidth; - if (textRegionLine->segments.size() == 1) - lineBaseXY = textRegionLine->baseOrigin; - - lastXY = movedGlyphPoint; - - return pass; -} - -void TextRegion::renderToTextFrame(PageItem* textNode) -{ - // nothing clever, just get all the body text in one lump and update the text frame - textNode->setWidthHeight(this->maxWidth, this->maxHeight); - QString bodyText = ""; - for (int glyphIndex = this->textRegionLines.begin()->glyphIndex; glyphIndex <= this->textRegionLines.back().segments.back().glyphIndex; glyphIndex++) - bodyText += glyphs[glyphIndex].code; - - textNode->itemText.insertChars(bodyText); - textNode->frameTextEnd(); -} - -bool TextRegion::isNew() -{ - return textRegionLines.empty() || - glyphs.empty(); -} - void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) { @@ -4219,9 +3943,8 @@ void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) int textRenderingMode = state->getRender(); // Invisible or only used for clipping if (textRenderingMode == 3) - { return; - } + // Fill text rendering modes. See above if (textRenderingMode == 0 || textRenderingMode == 2 || textRenderingMode == 4 || textRenderingMode == 6) { @@ -4248,10 +3971,10 @@ void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) if (textRenderingMode == 1 || textRenderingMode == 2 || textRenderingMode == 5 || textRenderingMode == 6) { CurrColorStroke = getColor(state->getStrokeColorSpace(), state->getStrokeColor(), &CurrStrokeShade); - if (textNode->isTextFrame()) { //fill colour sets the background colour for the frame not the fill colour fore the text + if (textNode->isTextFrame()) { //fill color sets the background color for the frame not the fill color fore the text textNode->setFillTransparency(1.0 - (state->getFillOpacity() > state->getStrokeOpacity() ? state->getFillOpacity() : state->getStrokeOpacity())); textNode->setLineTransparency(1.0); // this sets the transparency of the textbox border and we don't want to see it - textNode->setFillColor(CommonStrings::None); //TODO: Check if we override the stroke colour with the fill colour when there is a choice + textNode->setFillColor(CommonStrings::None); //TODO: Check if we override the stroke color with the fill color when there is a choice textNode->setLineColor(CommonStrings::None); textNode->setLineWidth(0);//line width doesn't effect drawing text, it creates a bounding box state->getTransformedLineWidth()); textNode->setFillBlendmode(getBlendMode(state)); @@ -4269,8 +3992,11 @@ void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) } } -/** - * \brief Updates current text position +/* + * Updates current text position and move to a position and or add a new glyph at the previous position. + * NOTE: If a rogue glyph is detected it means that PdfTextRecognition &co. has a bug between the moveTo and addGlyphAtPoint function calls. in theory addGlyphAtPoint should never fail. + * FIXME: render the textframe, this should be done after the document has finished loading the current page so all the layout fix-ups can be put in-place first + * FIXME: textRegion needs to support moveBackOneGlyph instead of my manual implementation in this function. */ void SlaOutputDev::updateTextPos(GfxState* state) { @@ -4290,7 +4016,7 @@ void SlaOutputDev::updateTextPos(GfxState* state) { QPointF glyphPosition = activeTextRegion->lastXY; activeTextRegion->lastXY.setX(activeTextRegion->lastXY.x() - activeTextRegion->glyphs.back().dx); - if (activeTextRegion->addGlyphAtPoint(glyphPosition, activeTextRegion->glyphs.back()) == TextRegion::FrameworkLineTests::FAIL) + if (activeTextRegion->addGlyphAtPoint(glyphPosition, activeTextRegion->glyphs.back()) == TextRegion::LineType::FAIL) qDebug("FIXME: Rogue glyph detected, this should never happen because the cursor should move before glyphs in new regions are added."); #ifdef DEBUG_TEXT_IMPORT else @@ -4298,82 +4024,73 @@ void SlaOutputDev::updateTextPos(GfxState* state) #endif } } - TextRegion::FrameworkLineTests lineTestResult = activeTextRegion->moveToPoint(newPosition); - if (lineTestResult == TextRegion::FrameworkLineTests::FAIL) - { - // FIXME: render the textframe, this should be done after the document has finished loading the current page so all the layout fix-ups can be put in-place first - renderTextFrame(); - - //Create and initilize a new TextRegion + TextRegion::LineType lineTestResult = activeTextRegion->moveToPoint(newPosition); + if (lineTestResult == TextRegion::LineType::FAIL) + { + renderTextFrame(); m_textRecognition.addTextRegion(); updateTextPos(state); } } - +/* +* render the textregion to a new PageItem::TextFrame, currently some hackjish defaults have been implemented there are a number of FIXMEs and TODOs +* FIXME: Paragraphs need to be implemented properly this needs to be applied to the charstyle of the default pstyle +* FIXME xcord and ycord need to be set properly based on GfxState and the page transformation matrix +* TODO: Implement paragraph styles +* TODO: Implement character styles and fonts. +* TODO Decide if we should be setting the clipshape of the POoLine values as is the case with other import implementations +*/ void SlaOutputDev::renderTextFrame() { - //TODO: Implement, this should all in based the framework //qDebug() << "_flushText() m_doc->currentPage()->xOffset():" << m_doc->currentPage()->xOffset(); - // Ignore empty strings auto activeTextRegion = &m_textRecognition.activeTextRegion; if (activeTextRegion->glyphs.empty()) - // We don't clear the glyphs any more or at least until the whole page has been rendered glyphs.clear(); return; qreal xCoor = m_doc->currentPage()->xOffset() + activeTextRegion->textRegioBasenOrigin.x(); qreal yCoor = m_doc->currentPage()->initialHeight() - (m_doc->currentPage()->yOffset() + (double)activeTextRegion->textRegioBasenOrigin.y() + activeTextRegion->lineSpacing); // don't know if y is top down or bottom up qreal lineWidth = 0.0; -#ifdef DEBUG_TEXT_IMPORT - qDebug() << "rendering new frame at:" << xCoor << "," << yCoor << " With lineheight of: " << activeTextRegion->lineSpacing << "Height:" << activeTextRegion->maxHeight << " Width:" << activeTextRegion->maxWidth; -#endif - /* colours don't get reset to CommonStrings::None often enough.*/ - int z = m_doc->itemAdd(PageItem::TextFrame, PageItem::Rectangle, xCoor, yCoor, 40, 40, 0, CommonStrings::None, CommonStrings::None /* this->CurrColorStroke*/);//, PageItem::ItemKind::InlineItem); + #ifdef DEBUG_TEXT_IMPORT + qDebug() << "rendering new frame at:" << xCoor << "," << yCoor << " With lineheight of: " << activeTextRegion->lineSpacing << "Height:" << activeTextRegion->maxHeight << " Width:" << activeTextRegion->maxWidth; + #endif + int z = m_doc->itemAdd(PageItem::TextFrame, PageItem::Rectangle, xCoor, yCoor, 40, 40, 0, CommonStrings::None, CommonStrings::None ); PageItem* textNode = m_doc->Items->at(z); ParagraphStyle& pStyle = (ParagraphStyle&)textNode->itemText.defaultStyle(); - // set some hackish parameters up at first, line spacing can be calculated from the cursor position changes pStyle.setLineSpacingMode(pStyle.AutomaticLineSpacing); pStyle.setHyphenationMode(pStyle.AutomaticHyphenation); - - // TODO: Implement these using the framework - finishItem(textNode); - // FIXME: Implement these using the framework - //_setFillAndStrokeForPdf(state, text_node); - //FIXME: Here's some dummy code for now with sensible defaults, looks like state wasn't even needed - + finishItem(textNode); + //_setFillAndStrokeForPdf(state, text_node); textNode->ClipEdited = true; textNode->FrameType = 3; textNode->setLineEnd(PLineEnd); textNode->setLineJoin(PLineJoin); textNode->setTextFlowMode(PageItem::TextFlowDisabled); - //textNode->setFillTransparency(1.0); - textNode->setLineTransparency(1.0); // this sets the transparency of the textbox border and we don't want to see it + textNode->setLineTransparency(1.0); textNode->setFillColor(CommonStrings::None); textNode->setLineColor(CommonStrings::None); - textNode->setLineWidth(0);//line width doesn't effect drawing text, it creates a bounding box state->getTransformedLineWidth()); + textNode->setLineWidth(0); textNode->setFillShade(CurrFillShade); - // Oliver Stieber 2020-06-11 Set text matrix... This need to be done so that the global world view that we rite out glyphs to is transformed correctly by the context matrix for each glyph, possibly anyhow. - // needs the way in whicvh we are handeling transformations for the page to be more concrete beofre this code can be implemented either here or somewhere else - /* FIXME: Setting the text matrix isn't supported at the moment + /* Oliver Stieber 2020-06-11 Set text matrix... This need to be done so that the global world view that we rite out glyphs to is transformed correctly by the context matrix for each glyph, possibly anyhow. + needs the way in which we are handling transformations for the page to be more concrete before this code can be implemented either here or somewhere else + FIXME: Setting the text matrix isn't supported at the moment QTransform text_transform(_text_matrix); text_transform.setMatrix(text_transform.m11(), text_transform.m12(), 0, text_transform.m21(), text_transform.m22(), 0, - first_glyph.position.x(), first_glyph.position.y(), 1); - */ - /* todo, set the global transform + first_glyph.position.x(), first_glyph.position.y(), 1); gchar *transform = sp_svg_transform_write(text_transform); text_node->setAttribute("transform", transform); g_free(transform); - */ - /*set the default charstyle to the style of the glyph, this needs fleshing out a little */ + */ int shade = 100; - //TODO: This needs to come from the framework - //QString CurrColorText = getColor(state->getFillColorSpace(), state->getFillColor(), &shade); - //TODO: replace this with the framework - //applyTextStyleToCharStyle(pStyle.charStyle(), _glyphs[0].style->getFont().family(), CurrColorText, _glyphs[0].style->getFont().pointSizeF());// *_font_scaling); + /* + * This code sets the font and style in a very simplistic way, it's been commented out as it needs to be updated to be used within PdfTextRecognition &co. + QString CurrColorText = getColor(state->getFillColorSpace(), state->getFillColor(), &shade); + applyTextStyleToCharStyle(pStyle.charStyle(), _glyphs[0].style->getFont().family(), CurrColorText, _glyphs[0].style->getFont().pointSizeF());// *_font_scaling); + */ CharStyle& cStyle = static_cast(pStyle.charStyle()); cStyle.setScaleH(1000.0); cStyle.setScaleV(1000.0); @@ -4382,39 +4099,24 @@ void SlaOutputDev::renderTextFrame() textNode->itemText.setDefaultStyle(pStyle); textNode->invalid = true; activeTextRegion->renderToTextFrame(textNode); - //FIXME: Paragraphs need to be implemented properly this needs to be applied to the charstyle of the default pstyle textNode->itemText.insertChars(SpecialChars::PARSEP, true); - //Set the shape so we don't clip all the text away. + /* + * This code can be used to set PoLine instead of setting the FrameShape if setting the PoLine is the more correct way of doing things. + * I have no idea of what the PoLine is at this time except for it changes when the shape is set and appears to be unit scales as opposed to percentage scaled FPointArray boundingBoxShape; boundingBoxShape.resize(0); boundingBoxShape.svgInit(); //doubles to create a shape, it's 100% textframe width by 100% textframe height - double bbosdoubles[32] = { 0,0 - ,0,0 - ,100,0 - ,100,0 - ,100,0 - ,100,0 - ,100,100 - ,100,100 - ,100,100 - ,100,100 - ,0,100 - ,0,100 - ,0,100 - ,0,100 - ,0,0 - ,0,0 - }; - boundingBoxShape.svgMoveTo(bbosdoubles[0], bbosdoubles[1]); + + boundingBoxShape.svgMoveTo(TextRegion::boundingBoxShape[0], TextRegion::boundingBoxShape[1]); for (int a = 0; a < 16; a += 2) { - boundingBoxShape.append(FPoint(bbosdoubles[a * 2], bbosdoubles[a * 2 + 1])); + boundingBoxShape.append(FPoint(TextRegion::boundingBoxShape[a * 2], TextRegion::boundingBoxShape[a * 2 + 1])); } boundingBoxShape.scale(textNode->width() / 100.0, textNode->height() / 100.0); - - textNode->SetFrameShape(32, bbosdoubles); + */ + textNode->SetFrameShape(32, TextRegion::boundingBoxShape); textNode->ContourLine = textNode->PoLine.copy(); m_doc->Items->removeLast(); @@ -4426,121 +4128,20 @@ void SlaOutputDev::renderTextFrame() } } -/*code mostly taken from importodg.cpp which also supports some line styles and more fill options etc...*/ -//FIXME: This needs to be implemented based on the framework +/* +* code mostly taken from importodg.cpp which also supports some line styles and more fill options etc... +*/ void SlaOutputDev::finishItem(PageItem* item) { item->ClipEdited = true; item->FrameType = 3; - //this requires that PoLine is set - //FPoint wh = getMaxClipF(&item->PoLine); - //item->setWidthHeight(wh.x(), wh.y()); - //item->Clip = flattenPath(item->PoLine, item->Segments); + /*code can be enabled when PoLine is set or when the shape is set as that sets PoLine + FPoint wh = getMaxClipF(&item->PoLine); + item->setWidthHeight(wh.x(), wh.y()); + item->Clip = flattenPath(item->PoLine, item->Segments); + */ item->OldB2 = item->width(); item->OldH2 = item->height(); item->updateClip(); item->OwnPage = m_doc->OnPage(item); - //item->setFillTransparency(1.0 - state->getFillOpacity() > state->getStrokeOpacity() ? state->getFillOpacity() : state->getStrokeOpacity()); - //item->setLineTransparency(1.0); -} - -PdfTextRecognition::PdfTextRecognition() -{ - m_textRegions.push_back(activeTextRegion); - setCharMode(AddCharMode::ADDFIRSTCHAR); -} - -PdfTextRecognition::~PdfTextRecognition() -{ -} - -void PdfTextRecognition::addTextRegion() -{ - activeTextRegion = TextRegion(); - m_textRegions.push_back(activeTextRegion); - setCharMode(PdfTextRecognition::AddCharMode::ADDFIRSTCHAR); -} - -void PdfTextRecognition::addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, POPPLER_CONST_082 Unicode* u, int uLen) -{ - - switch (this->m_addCharMode) - { - case AddCharMode::ADDFIRSTCHAR: - AddFirstChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); - break; - case AddCharMode::ADDBASICCHAR: - AddBasicChar(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); - break; - case AddCharMode::ADDCHARWITHNEWSTYLE: - AddCharWithNewStyle(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); - break; - case AddCharMode::ADDCHARWITHPREVIOUSSTYLE: - AddCharWithPreviousStyle(state, x, y, dx, dy, originX, originY, code, nBytes, u, uLen); - break; - } -} - - -bool PdfTextRecognition::isNewLineOrRegion(QPointF newPosition) -{ - return (activeTextRegion.collinear(activeTextRegion.lastXY.y(), activeTextRegion.textRegionLines.back().baseOrigin.y()) && - !activeTextRegion.collinear(newPosition.y(), activeTextRegion.lastXY.y())) - || (activeTextRegion.collinear(newPosition.y(), activeTextRegion.lastXY.y()) - && !activeTextRegion.isCloseToX(newPosition.x(), activeTextRegion.lastXY.x())); -} - -PdfGlyph PdfTextRecognition::AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) -{ - //qDebug() << "AddFirstChar() '" << u << " : " << uLen; - PdfGlyph newGlyph = PdfTextRecognition::AddCharCommon(state, x, y, dx, dy, u, uLen); - activeTextRegion.glyphs.push_back(newGlyph); - setCharMode(AddCharMode::ADDBASICCHAR); - - //only need to be called for the very first point - auto success = activeTextRegion.addGlyphAtPoint(QPointF(x, y), newGlyph); - if(success == TextRegion::FrameworkLineTests::FAIL) - qDebug("FIXME: Rogue glyph detected, this should never happen because the coursor should move before glyphs in new regions are added."); - return newGlyph; -} -PdfGlyph PdfTextRecognition::AddCharCommon(GfxState* state, double x, double y, double dx, double dy, Unicode const* u, int uLen) -{ - //qDebug() << "AddBasicChar() '" << u << " : " << uLen; - PdfGlyph newGlyph; - newGlyph.dx = dx; - newGlyph.dy = dy; - - // Convert the character to UTF-16 since that's our SVG document's encoding - for (int i = 0; i < uLen; i++) - { - newGlyph.code = static_cast(u[i]); - } - - newGlyph.rise = state->getRise(); - return newGlyph; -} - -PdfGlyph PdfTextRecognition::AddBasicChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) -{ - PdfGlyph newGlyph = AddCharCommon(state, x, y, dx, dy, u, uLen); - activeTextRegion.lastXY = QPointF(x, y); - activeTextRegion.glyphs.push_back(newGlyph); - return newGlyph; -} -/*TODO: Currently not implemented, just stub code*/ -PdfGlyph PdfTextRecognition::AddCharWithNewStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) -{ - //qDebug() << "AddCharWithNewStyle() '" << u << " : " << uLen; - auto newGlyph = AddCharCommon(state, x, y, dx, dy, u, uLen); - activeTextRegion.glyphs.push_back(newGlyph); - return newGlyph; -} - -/*TODO: Currently not implemented, just stub code*/ -PdfGlyph PdfTextRecognition::AddCharWithPreviousStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) -{ - //qDebug() << "AddCharWithPreviousStyle() '" << u << " : " << uLen; - auto newGlyph = AddCharCommon(state, x, y, dx, dy, u, uLen); - activeTextRegion.glyphs.push_back(newGlyph); - return newGlyph; } diff --git a/scribus/plugins/import/pdf/slaoutput.h b/scribus/plugins/import/pdf/slaoutput.h index 90ef9f3f6..e06c77c87 100644 --- a/scribus/plugins/import/pdf/slaoutput.h +++ b/scribus/plugins/import/pdf/slaoutput.h @@ -29,6 +29,7 @@ for which a new license (GPL+exception) is in place. #include "scribusview.h" #include "selection.h" #include "vgradient.h" +#include "pdftextrecognition.h" #if POPPLER_ENCODED_VERSION < POPPLER_VERSION_ENCODE(0, 73, 0) #include @@ -153,102 +154,7 @@ class AnoOutputDev : public OutputDev QStringList *m_importedColors; }; -/* PDF TextBox Framework */ -/* -* Holds all the details for each glyph in the text imported from the pdf file. -* -*/ -struct PdfGlyph -{ - double dx; // X advance value - double dy; // Y advance value - double rise; // Text rise parameter - QChar code; // UTF-16 coded character -}; - - -class TextRegionLine -{ -public: - qreal maxHeight = {}; - //we can probably use maxHeight for this. - qreal width = {}; - int glyphIndex = {}; - QPointF baseOrigin = QPointF({}, {}); - std::vector segments = std::vector(); - -}; - -class TextRegion -{ -public: - enum class FrameworkLineTests - { - FIRSTPOINT, - SAMELINE, - STYLESUPERSCRIPT, - STYLENORMALRETURN, - STYLEBELOWBASELINE, - NEWLINE, - ENDOFLINE, //TODO: Implement an end of line test - FAIL - }; - - QPointF textRegioBasenOrigin = QPointF({}, {}); - qreal maxHeight = {}; - qreal lineSpacing = { 1 }; - std::vector textRegionLines = std::vector(); - qreal maxWidth = {}; - QPointF lineBaseXY = QPointF({ }, { }); //updated with the best match left value from all the textRegionLines and the best bottom value from the textRegionLines.segments; - QPointF lastXY = QPointF({}, {}); - static bool collinear(qreal a, qreal b); - bool isCloseToX(qreal x1, qreal x2); - bool isCloseToY(qreal y1, qreal y2); - bool adjunctLesser(qreal testY, qreal lastY, qreal baseY); - bool adjunctGreater(qreal testY, qreal lastY, qreal baseY); - TextRegion::FrameworkLineTests linearTest(QPointF point, bool xInLimits, bool yInLimits); - TextRegion::FrameworkLineTests isRegionConcurrent(QPointF newPoint); - TextRegion::FrameworkLineTests moveToPoint(QPointF newPoint); - TextRegion::FrameworkLineTests addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph new_glyph); - void renderToTextFrame(PageItem* textNode); - std::vector glyphs; - bool isNew(); -}; - - -class PdfTextRecognition -{ -public: - PdfTextRecognition(); - ~PdfTextRecognition(); - - enum class AddCharMode - { - ADDFIRSTCHAR = 0, - ADDBASICCHAR = 1, - ADDCHARWITHNEWSTYLE = 2, - ADDCHARWITHPREVIOUSSTYLE = 3 - }; - void setCharMode(AddCharMode mode) - { - m_addCharMode = mode; - } - - TextRegion& activeTextRegion = TextRegion(); //faster than calling back on the vector all the time. - void addTextRegion(); - void addChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, POPPLER_CONST_082 Unicode* u, int uLen); - bool isNewLineOrRegion(QPointF newPosition); -private: - - std::vector m_textRegions = std::vector(); - AddCharMode m_addCharMode = AddCharMode::ADDFIRSTCHAR; - PdfGlyph AddCharCommon(GfxState* state, double x, double y, double dx, double dy, Unicode const* u, int uLen); - PdfGlyph AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); - PdfGlyph AddBasicChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); - PdfGlyph AddCharWithNewStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); - PdfGlyph AddCharWithPreviousStyle(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen); -}; class SlaOutputDev : public OutputDev { @@ -328,7 +234,7 @@ class SlaOutputDev : public OutputDev void endMarkedContent(GfxState *state) override; void markPoint(POPPLER_CONST char *name) override; void markPoint(POPPLER_CONST char *name, Dict *properties) override; - + //----- image drawing void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, GBool invert, GBool interpolate, GBool inlineImg) override; @@ -405,7 +311,7 @@ class SlaOutputDev : public OutputDev void createImageFrame(QImage& image, GfxState *state, int numColorComponents); - //PDF Textbox framework + //PDF Textbox framework void setFillAndStrokeForPDF(GfxState* state, PageItem* text_node); void updateTextPos(GfxState* state) override; void renderTextFrame(); From 3925017bab05dd9e9187af34e2e80cd37a28720f Mon Sep 17 00:00:00 2001 From: olivetthered <37796246+olivetthered@users.noreply.github.com> Date: Sun, 21 Jun 2020 06:08:27 +0100 Subject: [PATCH 13/13] fixed layout regression, made qDebug output conditional, added niotes on invariants fixed layout regression, this was caused by basing code on an outdated version of the positioning result returned by linearTest. , made qDebug output conditional based on a precompiler defin, added niotes on invariants --- .../plugins/import/pdf/pdftextrecognition.cpp | 51 ++++++++++++++----- scribus/plugins/import/pdf/slaoutput.cpp | 22 ++++++-- 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/scribus/plugins/import/pdf/pdftextrecognition.cpp b/scribus/plugins/import/pdf/pdftextrecognition.cpp index 4bfd02c3d..bf8613821 100644 --- a/scribus/plugins/import/pdf/pdftextrecognition.cpp +++ b/scribus/plugins/import/pdf/pdftextrecognition.cpp @@ -7,6 +7,10 @@ for which a new license (GPL+exception) is in place. #include "pdftextrecognition.h" +#ifndef DEBUG_TEXT_IMPORT + #define DEBUG_TEXT_IMPORT +#endif + /* * constructor, initialize the textRegions vector and set the addChar mode */ @@ -80,8 +84,9 @@ PdfGlyph PdfTextRecognition::AddCharCommon(GfxState* state, double x, double y, newGlyph.dy = dy; // Convert the character to UTF-16 since that's our SVG document's encoding + if (uLen > 1) - qDebug() << "AddBasicChar() '" << u << " : " << uLen; + qDebug() << "FIXME: AddBasicChar() '" << u << " : " << uLen; newGlyph.code = static_cast(u[uLen - 1]); newGlyph.rise = state->getRise(); return newGlyph; @@ -90,6 +95,7 @@ PdfGlyph PdfTextRecognition::AddCharCommon(GfxState* state, double x, double y, /* * Tell the text region to add a glyph so that line segments and regions be created * If the character being added is the first character in a textregion or after a change in positioning or styles or the end of a line +* The success == TextRegion::LineType::FAIL test is an invariant test that should never pass. if a rogue glyph is detected then it means there is a bug in the logic probably in TextRegion::addGlyphAtPoint or TextRegion::linearTest or TextRegion::moveToPoint */ PdfGlyph PdfTextRecognition::AddFirstChar(GfxState* state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, Unicode const* u, int uLen) { @@ -157,10 +163,11 @@ bool TextRegion::collinear(qreal a, qreal b) /* * like collinear but we allow a deviation of 6 text widths from between positions or 1 text width from the textregion's x origin +* FIXME: This should use the char width not linespacing which is y */ bool TextRegion::isCloseToX(qreal x1, qreal x2) { - //FIXME: This should use the char width not linespacing which is y + return (abs(x2 - x1) <= lineSpacing * 6) || (abs(x1 - this->textRegioBasenOrigin.x()) <= lineSpacing); } @@ -168,9 +175,8 @@ bool TextRegion::isCloseToX(qreal x1, qreal x2) * like collinear but we allow a deviation of 3 text heights downwards but none upwards */ bool TextRegion::isCloseToY(qreal y1, qreal y2) -{ - - return (y2 - y1) >= 0 && y2 - y1 <= lineSpacing * 3; +{ + return (y2 - y1) >= 0 && y2 - y1 <= lineSpacing * 3; } /* @@ -201,6 +207,8 @@ bool TextRegion::adjunctGreater(qreal testY, qreal lastY, qreal baseY) * is formatted and in those cases the linespace is taken to be twice the glyph width. * FIXME: This needs fixing when font support is added and the ascending and descending values for the font should be used instead of the glyphs width. * TODO: support LineType::STYLESUBSCRIPT +* TODO: support NEWLINE new paragraphs with multiple linespaces and indented x insteads of just ignoring the relative x position +* TODO: I don't know if the invariant qDebug cases should always report an error or only do so when DEBUG_TEXT_IMPORT is defined. My feeling is they should always report because it meanms something has happened that shouldn't have and it's useful feedback. */ TextRegion::LineType TextRegion::linearTest(QPointF point, bool xInLimits, bool yInLimits) { @@ -209,6 +217,10 @@ TextRegion::LineType TextRegion::linearTest(QPointF point, bool xInLimits, bool return LineType::FIRSTPOINT; else if (xInLimits) return LineType::SAMELINE; + #ifdef DEBUG_TEXT_IMPORT + else + qDebug() << "FIRSTPOINT/SAMELINE oops:" << "point:" << point << " textRegioBasenOrigin:" << textRegioBasenOrigin << " baseline:" << this->lineBaseXY << " lastXY:" << lastXY << " linespacing:" << lineSpacing << " textRegionLines.size:" << textRegionLines.size(); + #endif else if (adjunctLesser(point.y(), lastXY.y(), lineBaseXY.y())) return LineType::STYLESUPERSCRIPT; else if (adjunctGreater(point.y(), lastXY.y(), lineBaseXY.y())) @@ -216,12 +228,23 @@ TextRegion::LineType TextRegion::linearTest(QPointF point, bool xInLimits, bool return LineType::STYLENORMALRETURN; else return LineType::STYLESUPERSCRIPT; - else if(isCloseToX(point.x(), textRegioBasenOrigin.x()) && isCloseToY(point.y(), lastXY.y())) - if (textRegionLines.size() >= 2) - return LineType::NEWLINE; - else if (textRegionLines.size() == 1) - return LineType::NEWLINE; - qDebug() << "NEWLINE2 oops:" << point << ":" << textRegioBasenOrigin << ":" << lineSpacing; + else if (isCloseToX(point.x(), textRegioBasenOrigin.x())) + if (isCloseToY(point.y(), lastXY.y()) && !collinear(point.y(), lastXY.y())) + if (textRegionLines.size() >= 2) + return LineType::NEWLINE; + else if (textRegionLines.size() == 1) + return LineType::NEWLINE; + #ifdef DEBUG_TEXT_IMPORT + else + qDebug() << "NEWLINE oops2:" << "point:" << point << " textRegioBasenOrigin:" << textRegioBasenOrigin << " baseline:" << this->lineBaseXY << " lastXY:" << lastXY << "linespacing:" << lineSpacing << "textRegionLines.size:" << textRegionLines.size() << " textRegionLines[textRegionLines.size() - 2].width:" << textRegionLines[textRegionLines.size() - 2].width << " maxWidth:" << maxWidth; + #endif + #ifdef DEBUG_TEXT_IMPORT + else + qDebug() << "NEWLINE oops:" << "point:" << point << " textRegioBasenOrigin:" << textRegioBasenOrigin << " baseline:" << this->lineBaseXY << " lastXY:" << lastXY << "linespacing:" << lineSpacing << "textRegionLines.size:" << textRegionLines.size(); + #endif + #ifdef DEBUG_TEXT_IMPORT //This isn't an invariant case like the others, we actually expect this to happen some of the time + qDebug() << "FAILED with oops:" << "point:" << point << " textRegioBasenOrigin:" << textRegioBasenOrigin << " baseline:" << this->lineBaseXY <<" lastXY:"<< lastXY << " linespacing:" << lineSpacing << " textRegionLines.size:" << textRegionLines.size(); + #endif return LineType::FAIL; } @@ -324,10 +347,12 @@ TextRegion::LineType TextRegion::addGlyphAtPoint(QPointF newGlyphPoint, PdfGlyph QPointF movedGlyphPoint = QPointF(newGlyphPoint.x() + newGlyph.dx, newGlyphPoint.y() + newGlyph.dy); if (glyphs.size() == 1) { - lineSpacing = newGlyph.dx * 2; + lineSpacing = newGlyph.dx * 3; lastXY = newGlyphPoint; lineBaseXY = newGlyphPoint; - } + } else if (textRegionLines.size() == 1) + lineSpacing = maxWidth * 3; + LineType mode = isRegionConcurrent(newGlyphPoint); if (mode == LineType::FAIL) return mode; diff --git a/scribus/plugins/import/pdf/slaoutput.cpp b/scribus/plugins/import/pdf/slaoutput.cpp index ef850054c..8d2524f52 100644 --- a/scribus/plugins/import/pdf/slaoutput.cpp +++ b/scribus/plugins/import/pdf/slaoutput.cpp @@ -20,8 +20,9 @@ for which a new license (GPL+exception) is in place. #include "util_math.h" #include - -#define DEBUG_TEXT_IMPORT +#ifndef DEBUG_TEXT_IMPORT + #define DEBUG_TEXT_IMPORT +#endif namespace { // Compute the intersection of two paths while considering the fillrule of each of them. @@ -3444,11 +3445,16 @@ void SlaOutputDev::beginTextObject(GfxState *state) { pushGroup(); if (importTextAsVectors == false && !m_textRecognition.activeTextRegion.textRegionLines.empty()) { + #ifdef DEBUG_TEXT_IMPORT + qDebug("beginTextObject: m_textRecognition.addTextRegion()"); + #endif m_textRecognition.addTextRegion(); } } /* - * NOTE: If a rogue glyph is detected it means that PdfTextRecognition &co. has a bug between the moveTo and addGlyphAtPoint function calls. in theory addGlyphAtPoint should never fail. + * NOTE: The success == TextRegion::LineType::FAIL test is an invariant test that should never pass. if a rogue glyph is detected then it means there is a bug in the logic probably in TextRegion::addGlyphAtPoint or TextRegion::linearTest or TextRegion::moveToPoint + * TODO: Support merging of text boxes where beginTextObject and endTextObject have been called but really it's looking like it's just a new line + * maybe do a second pass before rendering and implement a merge function in pdfTectRecognition &co. */ void SlaOutputDev::endTextObject(GfxState *state) { @@ -3460,6 +3466,9 @@ void SlaOutputDev::endTextObject(GfxState *state) if (m_textRecognition.activeTextRegion.addGlyphAtPoint(glyphXY, m_textRecognition.activeTextRegion.glyphs.back()) == TextRegion::LineType::FAIL) { qDebug("FIXME: Rogue glyph detected, this should never happen because the cursor should move before glyphs in new regions are added."); } + #ifdef DEBUG_TEXT_IMPORT + qDebug("endTextObject: renderTextFrame"); + #endif renderTextFrame(); } else if (importTextAsVectors == false && !m_textRecognition.activeTextRegion.textRegionLines.empty()) { qDebug("FIXME:Rogue textblock"); @@ -3994,7 +4003,7 @@ void SlaOutputDev::setFillAndStrokeForPDF(GfxState* state, PageItem* textNode) /* * Updates current text position and move to a position and or add a new glyph at the previous position. - * NOTE: If a rogue glyph is detected it means that PdfTextRecognition &co. has a bug between the moveTo and addGlyphAtPoint function calls. in theory addGlyphAtPoint should never fail. + * NOTE: The success == TextRegion::LineType::FAIL test is an invariant test that should never pass. if a rogue glyph is detected then it means there is a bug in the logic probably in TextRegion::addGlyphAtPoint or TextRegion::linearTest or TextRegion::moveToPoint * FIXME: render the textframe, this should be done after the document has finished loading the current page so all the layout fix-ups can be put in-place first * FIXME: textRegion needs to support moveBackOneGlyph instead of my manual implementation in this function. */ @@ -4026,7 +4035,10 @@ void SlaOutputDev::updateTextPos(GfxState* state) } TextRegion::LineType lineTestResult = activeTextRegion->moveToPoint(newPosition); if (lineTestResult == TextRegion::LineType::FAIL) - { + { + #ifdef DEBUG_TEXT_IMPORT + qDebug("updateTextPos: renderTextFrame() + m_textRecognition.addTextRegion()"); + #endif renderTextFrame(); m_textRecognition.addTextRegion(); updateTextPos(state);