private void MakeWordsCentered(clsEPLDriver epl, int iLayout, int maxLineWidth) { int wordcount = 0; int firstword = 0; int widthsofar = 0; for (int i = iLayout; i >= 0; --i) { firstword = i; if (layout[i].lineNumber != layout[iLayout].lineNumber) { firstword = i + 1; break; } else { widthsofar += layout[i].Width(epl); wordcount++; } } /* add the spaces to the width */ widthsofar += (wordcount - 1) * epl.TextWidth(epl.Font, epl.MultiplierHoriz, epl.MultiplierVert, " "); System.Diagnostics.Debug.Assert(wordcount >= 2); int wordofst = (maxLineWidth - widthsofar) / 2; /* centers a couple of words in a line */ for (int i = firstword; i <= iLayout; ++i) { if (layout[i].isContent) { layout[i].Box = new Rectangle(layout[i].Box.X + wordofst, layout[i].Box.Y, layout[i].Box.Width, layout[i].Box.Height); } } }
public void Render(clsEPLDriver epl) { if (!bLayoutPrepared) { return; } Rectangle box; /* renders plainly */ for (int i = 0; i < layout.Length; ++i) { /* stop if we are already out of bounds */ if (layout[i].lineNumber >= lines_rendered) { break; } /* translate them to their intended location (so far, they're zero-based in the top-left corner of logical space) */ box = new Rectangle(layout[i].Box.Location, layout[i].Box.Size); box.Offset(userBounds.Location); /* output them, outlined in debugging mode, unadorned in productive builds */ epl.LabelText( box.X, box.Y, 0, epl.Font, epl.MultiplierHoriz, epl.MultiplierVert, false, layout[i].sX ); } }
public int Height(clsEPLDriver epl) { if (!gotBoundary) { Measure(epl); } return(Box.Height); }
public int Width(clsEPLDriver epl) { if (!gotBoundary) { Measure(epl); } return(Box.Width); }
private void MakeWordCentered(clsEPLDriver epl, int iLayout, int maxLineWidth) { int curwidth = layout[iLayout].Width(epl); int wordofst = (maxLineWidth - curwidth) / 2; layout[iLayout].Box = new Rectangle( layout[iLayout].Box.X + wordofst, layout[iLayout].Box.Y, layout[iLayout].Box.Width, layout[iLayout].Box.Height ); }
private void Measure(clsEPLDriver epl) { /* measures the word */ if (isContent) { Box = new Rectangle( 0, 0, epl.TextWidth(epl.Font, epl.MultiplierHoriz, epl.MultiplierVert, sX), epl.TextHeight(epl.Font, epl.MultiplierHoriz, epl.MultiplierVert, sX) ); } gotBoundary = true; }
private void subTokenize(string sX, clsEPLDriver epl) { bool inWord = false; string curWord = ""; int iStart = 0; /* splits the paragraph up into words and whitespace designators */ for (int I = 1; I <= sX.Length + 1; ++I) /* the +1 will trigger the "" case below and mark the end of the string; mid$ is one-based! */ { string cX; if (I > sX.Length) { cX = ""; } else { cX = sX.Substring(I - 1, 1); } switch (cX) { case " ": /* a space, end of word */ if (inWord) { AddWord(false, false, iStart, I - 1, curWord); inWord = false; curWord = ""; } AddWord(true, false, I, I, cX); break; case "-": /* a primordial hyphen ends a word, thus presenting an opportunity to break, but it will remain part of the syllable to its left */ if (inWord) { AddWord(false, false, iStart, I, curWord + cX); inWord = false; curWord = ""; } else { /* a standalone hyphen will add to the current word */ if (!inWord) { iStart = I; inWord = true; } curWord += cX; } break; case "": /* end of string, end of word */ if (inWord) { AddWord(false, false, iStart, I - 1, curWord); inWord = false; curWord = ""; } break; case "\n": /* a line break */ if (inWord) { AddWord(false, false, iStart, I - 1, curWord); inWord = false; curWord = ""; } AddWord(false, true, I, I, cX); break; default: /* we're in a word */ if (!inWord) { iStart = I; inWord = true; } curWord += cX; break; } } /* a line break is always added after the very last line to ensure the last line of justified text is not stretched */ AddWord(false, true, -1, -1, "\n"); /* -1 indicating this line break is artificial and has no representation in the original source string */ /* push all the tokens onto the stack for the convenience of the linebreaker algo */ for (int I = ctWords - 1; I >= 0; --I) { if (!words[I].isWhiteSpace) { tokens.Push(words[I]); } } }
public void CalculateLayout(string sX, clsEPLDriver epl, Rectangle Boundary) { /* trivia: */ ResetLayout(); if (sX == null || sX.Length <= 0) { return; } sX = sX.Replace(Environment.NewLine, new string(new char[] { '\n' })); /* first shalt thou create arrays of words */ ctWords = 0; subTokenize(sX, epl); if (ctWords <= 0) { return; } /* then shalt thou create thy font's formatting rules: */ userBounds = new Rectangle(Boundary.Location, Boundary.Size); Rectangle absBounds = new Rectangle(Boundary.Location, Boundary.Size); /* determine the height of a line of text and the width of a standard space */ Rectangle stdBox = new Rectangle( 0, 0, epl.TextWidth(epl.Font, epl.MultiplierHoriz, epl.MultiplierVert, " "), epl.TextHeight(epl.Font, epl.MultiplierHoriz, epl.MultiplierVert, " ") ); int spaceWidth = stdBox.Width; lineHeight = stdBox.Height; int maxLineWidth = absBounds.Width; /* prepare a hyphenator to be at our disposal when we need it */ var hypX = new sherm.core.hyphenation.cdoHyphenation(); //_hyphenatedversion = _hyphenationservice.HyphenateFailsafe(ContentToRender); /* then we make a non-prefetching, non-readahead flow layout */ int curLine = 0; /* line numbers start with 0 */ lines_rendered = 1; /* real-world line numbers start with 1 */ int runningWidth = 0; /* the space that is currently taken up in the current line */ int runningOfst = 0; /* the offset where the next word is to start on the current line */ int widthToAdd; int wordsInCurLine = 0; /* the number of words (not counting spaces) in the current line */ word curword; int distribution; //bool apply_inline_distribution=true; while (tokens.Count > 0) { /* ponder about the variants for this line...how many words are we going to fill in */ /* 1. get a word into the current line */ curword = tokens.Pop(); if (curword.isContent) { /* try to add this word to the current line */ widthToAdd = ((wordsInCurLine > 0) ? spaceWidth : 0) + curword.Width(epl); if (runningWidth + widthToAdd > maxLineWidth) { /* we need to break this word at the end of line * (or, in extreme case, at the beginning of the line, if this word alone takes up more space than the scanline can provide) */ distribution = maxLineWidth - runningWidth; if (distribution > maxLineWidth * cstHyphenationTreshold) { /* leaving as is would create too much white space. try fit the first hyphenation of the current word */ string[] parts = hypX.Hyphenate(curword.sX); word hyphtok = new word(); string shyphtok = ""; bool bsolved; bsolved = false; hyphtok.isContent = true; for (int i = 0; i < parts.Length; ++i) { /* try to fit this. */ shyphtok += parts[i]; hyphtok.sX = shyphtok + "-"; /* is it too much? then revert to hard-breaking (see stmts after end of loop) */ if (runningWidth + hyphtok.Width(epl) > maxLineWidth) { //apply_inline_distribution = false; /* this does the trick of not line-spanning the word(s) immedately before a forced break */ break; } /* it it too narrow still? then take the next syllable if there's one */ distribution = maxLineWidth - (runningWidth + hyphtok.Width(epl)); if (distribution <= maxLineWidth * cstHyphenationTreshold) { /* yep, this does it. employ it */ expandlayoutarray(layout.Length + 1); layout[layout.Length - 1] = hyphtok.Clone(); layout[layout.Length - 1].Offset(runningOfst, curLine, lineHeight); wordsInCurLine++; /* now our current word is the remainder of the hyphenated string */ shyphtok = ""; for (int j = i + 1; j < parts.Length; ++j) { shyphtok += parts[j]; } curword.sX = shyphtok; bsolved = true; break; } else { /* if this is not yet enough, and we have another hyphen in the word, we continue the loop */ } } /* the hyphenation approach could not make amends - * thy foe the unbreakable word who, by not obeying the laws of clean type setting, * being naughty in My sight, shall now snuff it. */ if ((!bsolved) && (wordsInCurLine <= 0)) { /* forced breaking is done with maximum fit, ie., we start chopping off glyphs from the right */ word breaktok = new word(); breaktok.isContent = true; for (int i = curword.sX.Length; i >= 0; --i) { /* try to fit this. */ breaktok.sX = curword.sX.Substring(0, Math.Max(i - 1, 1)); /* is it still too much? then take one glyph less, but take one at least */ if ((i <= 0) || (runningWidth + breaktok.Width(epl) <= maxLineWidth)) { /* yep, this does it. employ it */ expandlayoutarray(layout.Length + 1); layout[layout.Length - 1] = breaktok.Clone(); layout[layout.Length - 1].Offset(runningOfst, curLine, lineHeight); wordsInCurLine++; /* now our current word is the remainder of the broken string */ curword.sX = curword.sX.Substring(i); break; } else { /* if this is not yet narrow enough, and we have glyphs left to remove, we continue the loop */ } } } } /* the distribution must always take place, accidental perfect fit can be assumed to be too rare to optimise for it */ if (wordsInCurLine == 1) { /* center the word */ MakeWordCentered(epl, layout.Length - 1, maxLineWidth); } else if (wordsInCurLine > 1) /* && apply_inline_distribution removed [dlatikay 20081106] */ /* center the line */ { MakeWordsCentered(epl, layout.Length - 1, maxLineWidth); } /* for the next line, we will again presume that in-line-distribution is applicable */ //apply_inline_distribution = true; /* in this branch, in spite of what exactly we're devising above, we'll always cue with a new line */ curLine++; if (lines_rendered * lineHeight < userBounds.Height) { lines_rendered++; } runningWidth = 0; runningOfst = 0; wordsInCurLine = 0; /* the rest of the word, or the entire word in case we invoked a word-span-line, is returned to the donator stack */ tokens.Push(curword); } else { /* add the word as is */ expandlayoutarray(layout.Length + 1); layout[layout.Length - 1] = curword.Clone(); layout[layout.Length - 1].lineNumber = curLine; layout[layout.Length - 1].Box.Offset(new Point(runningOfst, curLine * lineHeight)); runningWidth += widthToAdd; runningOfst += curword.Width(epl) + spaceWidth; wordsInCurLine++; } } else if (curword.isLineBreak) { /* immediately go to the next line. if there is nothing to come, stop layouting * spaces are not in! */ if (tokens.Count <= 0) { /* we are at the end */ if (wordsInCurLine == 1) { /* center the word */ MakeWordCentered(epl, layout.Length - 1, maxLineWidth); } else if (wordsInCurLine > 1) /* && apply_inline_distribution removed [dlatikay 20081106] */ /* center the line */ { MakeWordsCentered(epl, layout.Length - 1, maxLineWidth); } break; } else { /* realise the return of the carriage and the feeding of the line */ curLine++; if (lines_rendered * lineHeight < userBounds.Height) { lines_rendered++; } runningWidth = 0; runningOfst = 0; wordsInCurLine = 0; } } else { /* spaces are not allowed in this place */ System.Diagnostics.Debug.Assert(false); } } /* complete */ effHeight = lines_rendered * lineHeight; bLayoutPrepared = true; }
public void JustifyText(string sX, clsEPLDriver epl, Rectangle boundary) { CalculateLayout(sX, epl, boundary); /* when the step number three, being the third step, be reached, then renderest thou thy utterings utterly onto the screen */ Render(epl); }
public bool SHEQPrintSDSLabel(clsLabelDetails label, labelprinterdrivers driver, string OSprintername, int number_of_copies) { bool inverse_images; /* idle? */ if (number_of_copies <= 0) { return(true); } try { /* does the driver exist? */ if (driver == labelprinterdrivers.lpd_zebratlp2844_orange) { inverse_images = false; } else if (driver == labelprinterdrivers.lpd_zebratlp2844_white) { inverse_images = true; } else { /* format not supported */ throw new ArgumentOutOfRangeException("driver", driver, "The label format specified is not supported by this version of the printer driver."); } /* treat the pictures */ int ctpictures = 0; Bitmap[] pictures = new Bitmap[max_pics_per_label]; Bitmap[] monochromes = new Bitmap[max_pics_per_label]; byte[][] rasters = new byte[max_pics_per_label][] { new byte[] { }, new byte[] { }, new byte[] { } }; if (label.Pictures == null) { ctpictures = 0; } else { ctpictures = label.Pictures.Length; } if (ctpictures > max_pics_per_label) { /* too many pictures */ throw new ArgumentOutOfRangeException("ctpictures", ctpictures, "This layout does not permit to print more than three pictures on a form."); } else { /* make them smoothly smaller */ for (int i = 0; i < ctpictures; ++i) { pictures[i] = new Bitmap(160, 160, PixelFormat.Format24bppRgb); using (var gfxX = Graphics.FromImage(pictures[i])) { gfxX.InterpolationMode = InterpolationMode.HighQualityBicubic; gfxX.DrawImage(label.Pictures[i], new Rectangle(0, 0, 160, 160), 0, 0, label.Pictures[i].Width, label.Pictures[i].Height, GraphicsUnit.Pixel); } } } /* convert any pictures we got */ for (int i = 0; i < ctpictures; ++i) { /* a. increase brightness */ for (int y = 0; y < pictures[i].Height; ++y) { for (int x = 0; x < pictures[i].Width; ++x) { if (pictures[i].GetPixel(x, y).GetBrightness() > 0) { pictures[i].SetPixel(x, y, Color.White); } } } /* b. from color to monochrome */ monochromes[i] = CopyToBpp(pictures[i], 1); /* c. from bmp to EPL raster */ rasters[i] = BitmapToEPLRaster(monochromes[i], inverse_images); } /* compose the EPL sequence */ const int width = 800; const int height = 560; clsEPLDriver epl = new clsEPLDriver(); epl.LabelStart(height, 19, width, "A", 49); /* 1. print the header */ epl.LabelText(30, 8, 0, 2, 1, 1, false, label.Clientcompany); epl.LabelTextRAlign(773, 8, 0, 2, 1, 1, false, label.Clientbusiness); epl.LabelLine(30, 25, 743, 3); /* 2. print the substance name */ int ypos = 50; int lineheight = epl.TextHeight(4, 2, 3, label.Name); epl.Font = 4; epl.MultiplierHoriz = 2; epl.MultiplierVert = 3; var renderer = new clsWordbreaker(); renderer.JustifyText(label.Name, epl, new Rectangle(10, ypos, width - 20, lineheight * 2)); #if DEBUG //epl.LabelLine(10,ypos+10,width-20,10); #endif ypos += renderer.effHeight; /* 3. print the manufacturer */ epl.LabelTextHCenter(width / 2, ypos, 0, 4, 1, 2, false, label.Manufacturer); /* frameborder for debugging purposes: * epl.LabelLine(0,0,width,20); * epl.LabelLine(0,height-20,width,20); * epl.LabelLine(0,0,20,height); * epl.LabelLine(width-20,0,20,height); */ /* 4. fill the orange rectangles with the pictures and their subscripts */ epl.Font = 1; epl.MultiplierHoriz = 1; epl.MultiplierVert = 1; int posfact = 225; int posofst = 92; for (int i = 0; i < ctpictures; ++i) { int xco = posofst + i * posfact; epl.LabelBitmap( xco, 315, rasters[i], monochromes[i].Width / 8 ); lineheight = epl.TextHeight(1, 1, 1, label.Subscripts[i]); renderer = new clsWordbreaker(); renderer.JustifyText(label.Subscripts[i], epl, new Rectangle(xco, 480, monochromes[i].Width, lineheight * 2)); } epl.LabelEnd(number_of_copies); /* send to the printer */ byte[] sequence = epl.ToPrinter; int hresult; bool result = clsDirectPrinting.SendByteArrayToPrinter(OSprintername, label.Name, sequence, out hresult); /* evaluate the result */ if (result) { return(true); } else { throw new Exception(String.Format("An error occurred whilst sending the print job \"{0}\" to the device \"{1}\".", label.Name, OSprintername)); } } catch (Exception ex) { /* some error */ throw new Exception(String.Format("An error occurred whilst preparing the print job \"{0}\".", label.Name), ex); } }