Страницы

Поиск по вопросам

Показаны сообщения с ярлыком ms-word. Показать все сообщения
Показаны сообщения с ярлыком ms-word. Показать все сообщения

четверг, 2 апреля 2020 г.

Создание акта в word C#

#c_sharp #winforms #ms_word #office_interop

                    
Доброго времени суток. Нужно создать акт в word на основе данных, имеющихся на winforms.
Подскажите, как лучше это сделать или с чего вообще начать? 
На листе будет отформатированный текст и таблица, число строк в которой каждый раз
будет разным, но не более 10.
Примерный внешний вид акта можно увидеть на фото ниже. Места, где замазано, как раз
надо будет заменить на свои. Ну и, как писал выше, добавить строки в таблицу. В изначальном
шаблоне есть только шапка таблицы, без строк.

    


Ответы

Ответ 1



Используй стандартное расширение Microsoft.Office.Interop.Word и подготовь шаблон с данного документа с закладками в требуемых местах. Краткий код работы с документом: Word._Document document; Word._Application application=new Word.Application(); object missingObj = Missing.Value; object templatePathObj = documentpath; //открытие файла try { document = application.Documents.Add(ref templatePathObj, ref missingObj, ref missingObj, ref missingObj); } catch { document.Close(ref falseobj, ref missingObj, ref missingObj); application.Quit(ref missingObj, ref missingObj, ref missingObj); document = null; application = null; } //заполнение закладок object bookmarkObj = "закладка"; Word.Range bookmarkRange = document.Bookmarks.get_Item(ref bookmarkObj).Range; bookmarkRange.Text = ""; //работа с таблицей Word.Table _table = _document.Tables[tableNumber]; //Выбрать уже существующую таблицу внутри документа можно по ее порядковому номеру (начиная с 1 и начала документа) _table.Rows.Add(ref _missingObj); Word.Range _currentRange = _table.Cell(rowIndex, columnIndex).Range; _currentRange.Text=""; //Вывод на печать document.PrintOut(ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj); document.Close(Word.WdSaveOptions.wdDoNotSaveChanges); application.Quit(); Подробно об этом расширении есть тут и тут.

пятница, 14 февраля 2020 г.

Почему Word открывается в режиме только для чтения

#c_sharp #com #ms_word #office_interop


Не могу понять в чем дело. Вроде бы скопировал у себя же рабочий код, но почему-то
не работает. А не работает потому что word открывается в режиме для чтения.
Хотя перед этим всё работало.

private void button5_Click(object sender, EventArgs e)
    {   
        //показываем панель для введения значений в бланк 
        panel2.Visible = true;
        //скрываем изображение 
        pictureBox1.Visible = false;

        //Создаём новый Word.Application
        Microsoft.Office.Interop.Word.Application app = new Microsoft.Office.Interop.Word.Application();

        //Загружаем документ
        Microsoft.Office.Interop.Word.Document doc = null;


        //вариант открытия существующего файла
        object fileName = "E:\\R1.rtf";
        object falseValue = false;
        object trueValue = false;
        object missing = Type.Missing;

        doc = app.Documents.Open(ref fileName, ref missing, ref trueValue,
        ref missing, ref missing, ref missing, ref missing, ref missing,
        ref missing, ref missing, ref missing, ref missing, ref missing,
        ref missing, ref missing, ref missing);

        //Теперь у нас есть документ который мы будем менять.

        //Очищаем параметры поиска
        app.Selection.Find.ClearFormatting();
        app.Selection.Find.Replacement.ClearFormatting();

        //Задаём параметры замены и выполняем замену нп поле варианта 
        object findText = "";
        object replaceWith = VarApply;
        object replace = 2;

        app.Selection.Find.Execute(ref findText, ref missing, ref missing, ref missing,
        ref missing, ref missing, ref missing, ref missing, ref missing, ref replaceWith,
        ref replace, ref missing, ref missing, ref missing, ref missing);
        app.Visible = false;

        doc.Save();
        doc.Close(Microsoft.Office.Interop.Word.WdSaveOptions.wdSaveChanges);
        app.Quit(false);
        richTextBox1.LoadFile("E:\\R1.rtf");

    }


Не хочет сохранять изменённый файл в строке doc.Save();
    


Ответы

Ответ 1



doc = app.Documents.Open(ref fileName, ref missing, ref trueValue, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing); Третий параметр - ReadOnly, поставьте его missing. Да и вообще можно написать кратко app.Documents.Open(ref fileName)

Как вставить шрифт в word документ?

#шрифты #ms_word


Я нашёл в интернете шрифт, который хочу использовать при написании текста в документе
Word.
Я могу этот шрифт добавить в Windows. Но, если я отправлю этот документ другому человеку,
у которого этот шрифт не установлен, то что будет?

Что надо: чтобы человек, который получил этот документ, видел текст в корректном
виде, без дополнительной установки шрифтов в Windows.

Это возможно? Если да, то как?
    


Ответы

Ответ 1



Внедрите шрифт, если вы хотите поделиться документом. Установив новый шрифт, вы сможете использовать его только на своем компьютере и принтере. Если вам нужно поделиться документом, содержащим новый шрифт, с каким-либо человеком, такой шрифт на другом компьютере отображаться не будет (только если этот шрифт не установлен на другом компьютере). Чтобы решить эту проблему, внедрите шрифт в документ; в этом случае шрифт будет отображаться на любом компьютере. Имейте в виду, что внедрение шрифта в документ приведет к увеличению размера файла документа (так как он будет включать файлы шрифтов). В Word нажмите «Файл» – «Параметры». Нажмите «Сохранение». Поставьте флажок у «Внедрить шрифты в файл». В выпадающем меню выберите открытый документ. Подумайте, хотите ли вы внедрить только те знаки, которые используются в текущем документе. Это позволит вам уменьшить размер файла документа, особенно если вы используете только несколько знаков нового шрифта. Сохраните и поделитесь документом с другими пользователями. Шрифты будут автоматически внедрены в документ при его сохранении. Источник: https://ru.wikihow.com/добавить-шрифт-в-Microsoft-Word

Обход объединенных ячеек в Word

#c_sharp #net #ms_word #office #office_interop


В общем, в продолжение своей предыдущей темы по парсингу таблиц Word'a с помощью
interop я столкнулся с объединенными ячейками.

Как я могу узнать на сколько ячеек произошло объединение, что бы я мог размножить
F3 на N строчек?



Капался в дебаггере в поисках нужного свойства, но что-то не нашел... Да и в интернете
поголовно пишут, что такого свойства нет

UPD 1

На одном из ресурсов подкинули вот такую идею(Код на VBA, но легко переписывается на C#):

Sub ParseInExcel()
    Dim doc As Document, tbl As Table, eObj, eWbk, eWst, eRng, eCell
    Dim i As Long, j As Long
    Set doc = ThisDocument
    Set tbl = doc.Tables(1)
    tbl.Range.Copy
    Set eObj = CreateObject("Excel.Application")
    Set eWbk = eObj.Workbooks.Add
    Set eWst = eWbk.Sheets(1)
    eWst.Paste
    Set eRng = eWst.Cells(1).CurrentRegion
    For i = 1 To eRng.Rows.Count
        For j = 1 To eRng.Columns.Count
            Set eCell = eRng.Cells(i, j)
            If eCell.MergeCells Then
                Debug.Print eCell.MergeArea.Cells(1).Value & " ";
            Else
                Debug.Print eCell.Value & " ";
            End If
        Next
        Debug.Print
    Next
    eWbk.Saved = True
    eWbk.Close
    Set eWbk = Nothing
    eObj.Quit
    Set eObj = Nothing
End Sub


Смысл в том, что из Word все копипастится в Excel, а там уже появляются свойства,
которые показывают объединение ячеек-MergeRange. Проблема в том, что будет занят буфер
обмена(самое проблемное на мой взгляд, так как не смогу работать за ПК в момент работы
программы иначе покалечу буфер), а так же в том, что подключается второй всадник апокалипсиса-Excel.

Если Excel знает, как представить таблицу из Word и создать свойства, которые показывают,
что некая зона объединена, то значит должен иметься какой-то однозначный алгоритм для
вычисления этого средствами Word'a.

Даже при сохранении в html-формате пишется кол-во объединенных колонок и строк.

UPD 2

По объединенным строкам я нашел решение, но по колонкам нет. Накидал свое решение,
но не думаю, что оно универсально. 

Может у кого-то есть еще идеи?

P.S Interop не принципиален, но в приоритете, так как много кода на нем нарисовал.

Если есть решение например для *.DOCX, то я могу сменить формат.
    


Ответы

Ответ 1



Получим xml с docx это не сложно вот Давайте рассмотрим такую таблицу 1 4 < 5 2 - - ^ Где < и ^ - направление обьединения ячеек. Тогда получим следующее (оставляю только значащие теги) (1) (4) (5) (2) - - Текст хранится в тегах текст. Вычислить обьеденённый столбец этим способом легче - по тегу gridSpan. Обьеденённые строки прийдется перелистывать vMerge тег. Вот пример как получить доступ к тегам внутри docx using System; using System.IO; using System.Xml; public class Demo { public static void docx2process(Stream file) { int isTable = 0; /*Для парсинга xml*/ int col = 0; int row = 0; int colSpan = 1; /*Парсинг архива*/ int ready = 0; while (ready++ < 10){ /*защита от повисания, обход архива*/ byte[] head = new byte[30]; file.Read(head, 0, 30); if (head[0] != 'P') break; //zip-header int i = (head[27] + head[29]) * 256 + head[28]; // extra len long paked = BitConverter.ToInt32(head,18); byte[] nam = new byte[255]; file.Read(nam, 0, head[26]); if (i != 0) file.Seek(i, SeekOrigin.Current); String aname = System.Text.Encoding.ASCII.GetString(nam, 0, head[26]); if (aname == "word/document.xml"){ long lastpos = file.Position; using (System.IO.Compression.DeflateStream deflate = new System.IO.Compression.DeflateStream(file, System.IO.Compression.CompressionMode.Decompress)){ System.Xml.XmlReader rd = System.Xml.XmlReader.Create(deflate); // Тут парсинг документа while (rd.Read()){ if (rd.NodeType == XmlNodeType.Element) switch (rd.Name){ case "w:gridSpan": colSpan = int.Parse(rd.GetAttribute("w:val"));break; case "w:vMerge":; /*Тут нужно допилить обьединение строк*/ break; case "w:tbl": row=0; isTable = rd.Depth ; break; case "w:tr": col=0; row++; break; case "w:tc": col+= colSpan; colSpan=1; break; case "w:t":/*Теги с текстом*/ if (rd.Read() && rd.NodeType == XmlNodeType.Text) /*Тут нужно написать свой вывод куда-либо*/ if (isTable>0) Console.WriteLine(string.Format("c={0}.{3} r={1} t={2}",col,row,rd.Value, colSpan)); else Console.WriteLine(rd.Value); break; case "w:p": /*Параграф*/ if (isTable == rd.Depth) isTable = 0; break; } } return; } file.Position = lastpos + paked; }else file.Seek(paked, SeekOrigin.Current); }; } public static void docx2process(string filename) { using (Stream f = File.OpenRead(filename)) docx2process(f); } public static void Main(string[] args) { docx2process("1.docx"); } } этот пример почти решает поставленую задачу. Обьедененные столбцы - видно. Обьеденённые строки - ещё нужно доработать.

Ответ 2



Изучение вот этого простого кода Sub test() Dim oneTable As Table Dim oneCell As Cell Dim i As Integer Set oneTable = ThisDocument.Tables(1) For Each oneCell In oneTable.Range.Cells i = i + 1 Debug.Print i, oneCell.RowIndex, oneCell.ColumnIndex Next End Sub на тестовых табличках с объединением ячеек даст достаточно информации для решения проблемы.

Ответ 3



Даже при сохранении в html-формате пишется кол-во объединенных колонок и строк. Так может и воспользоваться этой деталью? Перегнать doc в html и распарсить атрибут colspan/rowspan using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Text; using Microsoft.Office.Interop.Word; using System.Runtime.InteropServices; //Add Reference: Microsoft HTML Object Library namespace ConsoleApplication1 { class Program { public static List FindMergedColumns(string html) { List res = new List(); mshtml.HTMLDocument doc = null; mshtml.IHTMLDocument2 d2 = null; mshtml.IHTMLDocument3 d = null; try { doc = new mshtml.HTMLDocument(); d2 = (mshtml.IHTMLDocument2)doc; d2.write(html); d2.close(); d = (mshtml.IHTMLDocument3)doc; var coll = d.getElementsByTagName("table"); object val; int numtable = 1; int row = 1, column = 1; int span; foreach (mshtml.IHTMLElement2 table in coll) { var rows = table.getElementsByTagName("tr"); foreach (mshtml.IHTMLElement2 tr in rows) { var cells = tr.getElementsByTagName("td"); foreach (mshtml.IHTMLElement td in cells) { val = td.getAttribute("colspan"); if(val == null)val = 0; span = Convert.ToInt32(val); if (span > 1) { res.Add(String.Format("Table {0}, Row {1}, Column {2}: {3} columns merged",numtable,row,column,span)); } column++; } row++; column = 1; } numtable++; row = 1; column = 1; } doc.close(); } finally { //освобождение ресурсов if (doc != null) Marshal.ReleaseComObject(doc); if (d2 != null) Marshal.ReleaseComObject(d2); if (d != null) Marshal.ReleaseComObject(d); } return res; } public static void Main(string[] argv) { var word = new Microsoft.Office.Interop.Word.Application(); object miss = System.Reflection.Missing.Value; object path = "c:\\test\\test.doc"; object readOnly = true; var docs = word.Documents.Open(ref path, ref miss, ref readOnly, ref miss, ref miss, ref miss, ref miss, ref miss, ref miss, ref miss, ref miss, ref miss, ref miss, ref miss, ref miss, ref miss); string tmp = Path.GetTempPath() + "file.htm"; //конвертируем doc в html docs.SaveAs(FileName: tmp, FileFormat: WdSaveFormat.wdFormatHTML); ((_Document)docs).Close(); ((_Application)word).Quit(); //парсим HTML string html = File.ReadAllText(tmp); var res = FindMergedColumns(html); File.Delete(tmp); foreach (var line in res) Console.WriteLine(line); Console.ReadKey(); } } }

Ответ 4



Обратите внимание на строки: WordTableAnalyser wta = new WordTableAnalyser(table,1.0); MyWordCell c = wta.getCell(8, 1); MessageBox.Show(c.rows.ToString() + ";" + c.columns.ToString()); Исходная таблица Приложение на основе Windows Forms using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using Microsoft.Office.Interop.Word; using System.Reflection; namespace WorkWithWord { using Word = Microsoft.Office.Interop.Word; public class MyWordCell { public double x = 0; public double x2 = 0; public double w = 0; public int columns = 1; public int rows = 1; public int ini_row=-1; public int ini_column=-1; public Word.Cell wc; public MyWordCell(Word.Cell cell) { wc = cell; } } public class DoubleToleranceComparer : IComparer,IEqualityComparer { double tolerance=0; public DoubleToleranceComparer(double tolerance) { this.tolerance = tolerance; } #region Члены IComparer public int Compare(double x, double y) { double delta = x - y; return Math.Abs(delta) <= tolerance ? 0 : Math.Sign(delta); } #endregion #region Члены IEqualityComparer public bool Equals(double x, double y) { throw new NotImplementedException(); } public int GetHashCode(double obj) { throw new NotImplementedException(); } #endregion } public class WordTableAnalyser { public static Object missingObj = System.Reflection.Missing.Value; public static Object trueObj = true; public static Object falseObj = false; Word.Table wtable = null; double cc = 0;//количество необъединенных столбцов double rc = 0;//количество необъединенных строк double WT = 0;//ширина таблицы double HT = 0;//высота таблицы DoubleToleranceComparer dt_comparer;//сравниватель double c допуском SortedList split_xs;//встречающиеся координаты границ ячеек таблицы по x List curRow = new List(); List> myTable = new List>(); public MyWordCell getCell(int ini_row,int ini_column) { MyWordCell c = null; for (int ci = 0; ci < myTable[ini_row-1].Count; ++ci) { if (myTable[ini_row - 1][ci].ini_column == ini_column) { c = myTable[ini_row - 1][ci]; break; } } return c; } public WordTableAnalyser(Word.Table table,double tolerance) { wtable = table; cc = table.Columns.Count; rc = table.Rows.Count; dt_comparer = new DoubleToleranceComparer(tolerance); split_xs = new SortedList(dt_comparer); split_xs.Add(0.0, 0.0); for (int i = 1; i <= cc; ++i) { try { Word.Cell cell=table.Cell(1,i); MyWordCell myCell = new MyWordCell(cell); myCell.x = WT; myCell.w = cell.PreferredWidth; myCell.x2 = myCell.x + myCell.w; myCell.ini_row = 1; myCell.ini_column = i; curRow.Add(myCell); WT += cell.PreferredWidth;//Width не всегда определена //cell. split_xs.Add(WT, WT); } catch(Exception ex) { } } myTable.Add(curRow); for(int j=2;j<=rc;++j) { List prevRow = curRow; curRow = new List(); List myTableRow = new List(); int prevColInd = 0; double curX=0; for (int i = 1; i <= cc; ++i) { try { Word.Cell cell=table.Cell(j,i); MyWordCell myCell = new MyWordCell(cell); myCell.x = curX; myCell.w = cell.PreferredWidth; myCell.x2 = myCell.x + myCell.w; myCell.ini_row = j; myCell.ini_column = i; curRow.Add(myCell); //while(myTable.Count 1) c.columns += delta_ind - 1; } } } } public partial class Form1 : Form { public static Object missingObj = System.Reflection.Missing.Value; public static Object trueObj = true; public static Object falseObj = false; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } private void buttonTest_Click(object sender, EventArgs e) { Word._Application application; Word._Document document=null; //создаем обьект приложения word application = new Word.Application(); // создаем путь к файлу Object templatePathObj = @"D:\Work\Life\StackOverflow\Разбиение объединённых ячеек в таблице.docm"; ; // если вылетим не этом этапе, приложение останется открытым try { document = application.Documents.Add(ref templatePathObj, ref missingObj, ref missingObj, ref missingObj); Word.Table table = document.Tables[1];//в файле примера одна единственная таблица int rcount = table.Rows.Count; int ccount = table.Columns.Count; WordTableAnalyser wta = new WordTableAnalyser(table,1.0); MyWordCell c = wta.getCell(8, 1); MessageBox.Show(c.rows.ToString() + ";" + c.columns.ToString()); application.Visible = true; } catch (Exception error) { if(document!=null)document.Close(ref falseObj, ref missingObj, ref missingObj); application.Quit(ref missingObj, ref missingObj, ref missingObj); document = null; application = null; MessageBox.Show(error.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } } } }

Ответ 5



Поиски привели меня на сайт, где я нашел такое решение для определения объеденных строчек: int GetRowSpan(Cell cell) { cell.Select(); return (int)_wordApp.Selection.Information[WdInformation.wdEndOfRangeRowNumber] - (int)_wordApp.Selection.Information[WdInformation.wdStartOfRangeRowNumber] + 1; } К сожалению, ничего подобного я не нашел для объеденных колонок... Написал свое решение, но не думаю, что оно универсальное: int GetColumnSpan(WordTable table, Cell cell) { var columnIndex = cell.ColumnIndex; var etalonSize = table.Cell(1, columnIndex).Width; if (cell.Width - etalonSize < 0.5) { return 1; } var curWidth = cell.Width; var mergCount = 1; while (curWidth - etalonSize > 0.5) { curWidth -= etalonSize; mergCount++; } return mergCount; } Суть в том, что на вход я подаю эталонную ширину ячейки, а затем сравниваю ее с текущей. Если ширина болье => ячейка объеденена... Если у кого-то есть мысли лучше, то пишите.

пятница, 27 декабря 2019 г.

Как на основе таблички сделать график в документе Word?

#delphi #com #ms_word


Я создаю в Word небольшую табличку, а затем мне надо на её основе сделать график.
Я нашел как добавить график, но код все время крашится с ошибкой в позиции

w := wrd.ActiveDocument.Shapes.AddOLEObject('MSGraph.Chart.8');


и график создается, но создается не на основе данных в таблице. Как сделать правильно?

procedure TForm1.btn2Click(Sender: TObject);
var
  wrd, w, Child: OleVariant;
  i: Integer;
  StartRange: Variant;
begin
  wrd:=CoWordApplication.Create;
  wrd.Visible:=true;
  wrd.Documents.Add;
  wrd.ActiveDocument.Tables.Add(wrd.ActiveDocument.Range(0, 0), 5, 2);
  wrd.Visible := True;
  For i:=1 to 5 do
    begin
      wrd.ActiveDocument.Tables.Item(1).Cell(i,1).Range.Text := i;
      wrd.ActiveDocument.Tables.Item(1).Cell(i,2).Range.Text := i*i;
    end;
  w:=wrd.ActiveDocument.Shapes.AddOLEObject('MSGraph.Chart.8');
  Child:=w.DiagramNode.Children.AddNode;
  for i:=1 to 5 do begin
    Child.Children.AddNode;
  end;
end;

    


Ответы

Ответ 1



Сейчас под рукой нет связки Delphi+Office, ответ даю по памяти и своим старым программам. Нет, ошибка у вас идёт в следующей строке, а не там, где вы предполагаете, поскольку код Child:=w.DiagramNode.Children.AddNode; обращается к несуществующему объекту. Чтобы создать диаграмму из имеющихся табличных данных, нужно, фактически, выделить таблицу (очень помогает запись макросов с последующим анализом) и затем применить уже к выделению метод InlineShapes.AddOLEObject. Как-то так: for i:=1 to 5 do begin wrd.ActiveDocument.Tables.Item(1).Cell(i,1).Range.Text := i; wrd.ActiveDocument.Tables.Item(1).Cell(i,2).Range.Text := i*i; end; // Тут кончается ваш код wrd.ActiveDocument.Tables.Item(1).Select; // выбираем вашу таблицу wrd.Selection.InlineShapes.AddOLEObject('MSGraph.Chart.8','',FALSE,FALSE); // вставляем диаграмму по данным таблицы. // Я использую только первые 4 параметра, смотрите сами по ссылке выше, какие и сколько вам нужны Данные вашей таблицы должны были быть скопированы в объект MS Graph. Дальше вы с ними можете работать. Например, так: Doc:=wrd.ActiveDocument; // ну, мне так проще :) Diag:=Doc.InlineShapes.Item(Doc.InlineShapes.Count); // ещё упрощаем доступ, вдруг понадобится доступ к объекту диаграммы в целом Chart:= Diag.OLEFormat.Object; //а теперь только Chart, только хардкор :) Chart.ChartType := 54; // ну, а дальше делаем, что хотим :) Chart.Application.DataSheet.Activate; Chart.Application.DataSheet.Cells.Clear; Chart.ChartArea.Font.Bold := False; Chart.Application.DataSheet.Cells[1, 1].Value :='11'; Разумеется, Doc, Diag и Chart - переменные типа OleVariant.

понедельник, 23 декабря 2019 г.

Меняю цвет заголовков и оглавления, но когда в Word нажимаю “обновить оглавление”, цвет сбрасывается

#delphi #delphi_7 #com #ms_word


У меня есть кнопка, при нажатии на которую создаются пара заголовков, а потом, на
их основе создается оглавление. Я меняю цвет заголовков, и меняю цвет оглавления, но
потом, когда в файле Word нажимаю "обновить оглавление", установленный цвет оглавления
сбрасывается. Как сделать так, чтобы цвет оглавления не сбрасывался, а оставался?

procedure TForm1.Button1Click(Sender: TObject);
var
  wrd:OleVariant;
  StartRange, tableOfContentsRange: Variant;
  tableOfContents: Variant;
begin
  wrd:=CoWordApplication.Create;
  wrd.Visible:=true;
  wrd.documents.add(EmptyParam,EmptyParam, EmptyParam,EmptyParam);
  wrd.Selection.Style := 'Заголовок 1';
  wrd.Selection.Font.Color := RGB(255,0,0);
  wrd.Selection.Text:='Первый Заголовок';
  wrd.Selection.MoveDown;
  wrd.Selection.TypeParagraph;
  wrd.Selection.Style := 'Заголовок 1';
  wrd.Selection.Font.Color := RGB(255,0,0);
  wrd.Selection.Text:='Второй заголовок';
  wrd.Selection.MoveDown;
  wrd.Selection.TypeParagraph;

  StartRange := wrd.ActiveDocument.Range(0, 0);
  StartRange.Font.Color:=RGB(0,255,0);
  tableOfContents := wrd.ActiveDocument.TablesOfContents.Add(StartRange);
  tableOfContentsRange := tableOfContents.Range;
  tableOfContentsRange.Collapse;
  tableOfContentsRange.Select;
  wrd.Selection.TypeText('Оглавление' + #13);
  tableOfContents.Update;
end;

    


Ответы

Ответ 1



Все изменения, которые представлены в Вашем коде, применяются непосредственно к тексту. В вашем случае нужно изменять данные непосредственно у стилей, которым предстоит отображать оглавление (поскольку обновление оглавления практически "создает" его заново). Обратите внимание, что стили оглавления и стили основного текста - это разные вещи (хоть и могут ссылаться друг на друга), поэтому изменение одного скорее всего ни коим образом не повлияет на другое. Ниже - переделанный метод с достаточно подробными комментариями: procedure TForm8.btn1Click(Sender: TObject); var wrd, Document: OleVariant; Selection: Variant; tableOfContents: Variant; TOC1Style: OleVariant; begin // общий принцип - при работе с Word и другими приложениями из MS Office, // если какой-либо объект используется более 1 раза, то лучше // завести для него отдельную переменную и работать уже с ней. // иначе конструкции типа // wrd.ActiveDocument.ActiveWindow.Selection.блабла1 // wrd.ActiveDocument.ActiveWindow.Selection.блабла2 // будут приводить к тому, что КАЖДЫЙ раз сперва будет вызываться wrd на предмет получения ActiveDocument, // потом ActiveDocument чтобы получить ActiveWindow ... ну и так далее. // Всё это, получаемое многократно, может замедлить и без того не быструю работу с MS Office. // Помимо прочего это в некоторой степени ограждает от действий пользователя // к примеру, пока работает ваша программа, пользователь может клацнуть по другому документу // соответственно wrd.ActiveDocument будет уже указывать не на нужный нам документ. wrd := CoWordApplication.Create; wrd.Visible := true; Document := wrd.documents.add; Selection := Document.ActiveWindow.Selection; Selection.Style := wdStyleHeading1; // для установки стилей лучше использовать // вот такие встроенные константы. Иначе есть риск напороться на иноязычный // Office, в котором "верхний" стиль будет называться к примеру 'Header 1' Selection.Font.Color := wdColorRed; Selection.Text := 'Первый Заголовок'; Selection.MoveDown; Selection.TypeParagraph; Selection.Style := wdStyleHeading1; Selection.Font.Color := wdColorRed; Selection.Text := 'Второй заголовок'; Selection.MoveDown; Selection.TypeParagraph; // меняем стиль "Оглавление 1". В этом случае также лучше пользоваться константами. TOC1Style := Document.Styles.Item(wdStyleTOC1); TOC1Style.AutomaticallyUpdate := true; TOC1Style.Font.Color := wdColorGreen; // хочу зелененькое :) TOC1Style.Font.Italic := true; TOC1Style.Font.Bold := true; // создаем содержание tableOfContents := Document.TablesOfContents.add( Range := Document.Range(0, 0), RightAlignPageNumbers := true, UseHeadingStyles := true, UpperHeadingLevel := 1, // применяя стили заголовков от 1 (изменен нами) LowerHeadingLevel := 3, // до 3 IncludePageNumbers := true, AddedStyles := '', UseHyperlinks := true, HidePageNumbersInWeb := true, UseOutlineLevels := true); Document.Range(0, 0).Select; Selection.TypeText('Оглавление' + #13#10); Selection.Move(wdStory, 1); tableOfContents.Update; // это излишне, содержание после создания // обновляется автоматически. Вот если бы мы после его создания // изменили основное содержимое, тогда да, обновить содержание не помешает. end;

понедельник, 16 декабря 2019 г.

Код создания документа word вызывает ошибку проводника explorer.exe

#c_sharp #winforms #ms_word


Мой код (c# winforms vs2010) после закрытия вызывает появление ошибки проводника
"explorer.exe" (на компьютерах с winxp и win7):

Подскажите пожалуйста, из-за чего может возникать ошибка проводника?
Привожу фрагмент кода:

    private void btn_Click(object sender, EventArgs e)
        {
            Word.Application word = null;
            Word.Documents docs = null;
            Word.Document doc = null;
            Word.Table oTable1 = null;
            Word.Table oTable = null;
            Word.Range oRange = null;
            Word.Paragraph oParag = null;
            Word.Paragraph oPara2 = null;
            Word.Template oTpl = null;

            try
            {
                if (Process.GetProcessesByName("WINWORD").Length == 0)
                {
                    word = new Word.Application();
                }
                else
                {
                    word = (Word.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Word.Application");
                }

                word.Visible = true;
                word.WindowState = Word.WdWindowState.wdWindowStateMinimize;

                docs = (Word.Documents)word.Documents;
                object templateName = @"c:\a3.dot";
                object missing = Type.Missing;
                object openVisible = true;
                object documentType = Word.WdDocumentType.wdTypeDocument;
                doc = docs.Add(ref templateName, ref missing, ref documentType, ref
openVisible);
                doc.ActiveWindow.WindowState = Word.WdWindowState.wdWindowStateMinimize;

                waitForm = new Form4();
                waitForm.TopMost = true;
                waitForm.StartPosition = FormStartPosition.CenterParent;
                waitForm.Show();
                if (waitForm.StartPosition == FormStartPosition.CenterParent)
                {
                    var x = Location.X + (Width - waitForm.Width) / 2;
                    var y = Location.Y + (Height - waitForm.Height) / 2;
                    waitForm.Location = new Point(Math.Max(x, 0), Math.Max(y, 0));
                }
                waitForm.Refresh();

//........

                object oRngEnd = word.Selection.GoTo(Word.WdGoToItem.wdGoToLine,
Word.WdGoToDirection.wdGoToLast);
                object oAutoText = "table";
                object objTrue = true;
                oTpl = (Word.Template)doc.get_AttachedTemplate();
                oPara2 = doc.Paragraphs.Add(oRngEnd);
                oPara2.Range.Text = "";
                oPara2.Range.Font.Size = 11;
                doc.Paragraphs.Add(oRngEnd);
                doc.Paragraphs.Add(oRngEnd);
                doc.Paragraphs.Add(oRngEnd);
                doc.Paragraphs.Add(oRngEnd);
                oPara2.Range.Select();
                oTpl.AutoTextEntries.get_Item(ref oAutoText).Insert(word.Selection.Range,
ref objTrue);

                word.Selection.GoTo(Word.WdGoToItem.wdGoToLine, Word.WdGoToDirection.wdGoToFirst);

                object oTemplate_null = "";
                doc.set_AttachedTemplate(oTemplate_null);

                doc.ActiveWindow.View.DisplayPageBoundaries = true;
                doc.ActiveWindow.ActivePane.View.Zoom.PageFit = Word.WdPageFit.wdPageFitBestFit;
                doc.ActiveWindow.WindowState = Word.WdWindowState.wdWindowStateMaximize;

                waitForm.Close();

                doc.Activate();
                word.Activate();
            }
            catch
            {
                waitForm.Close();

                if (oTpl != null) Marshal.FinalReleaseComObject(oTpl); oTpl = null;
                if (oPara2 != null) Marshal.FinalReleaseComObject(oPara2); oPara2 = null;
                if (oParag != null) Marshal.FinalReleaseComObject(oParag); oParag = null;
                if (oRange != null) Marshal.FinalReleaseComObject(oRange); oRange = null;
                if (oTable != null) Marshal.FinalReleaseComObject(oTable); oTable = null;
                if (oTable1 != null) Marshal.FinalReleaseComObject(oTable1); oTable1
= null;
                if (doc != null) Marshal.FinalReleaseComObject(doc); doc = null;
                if (docs != null) Marshal.FinalReleaseComObject(docs); docs = null;
                if (word != null) Marshal.FinalReleaseComObject(word); word = null;

                return;
            }
            finally
            {
                if (oTpl != null) Marshal.FinalReleaseComObject(oTpl); oTpl = null;
                if (oPara2 != null) Marshal.FinalReleaseComObject(oPara2); oPara2 = null;
                if (oParag != null) Marshal.FinalReleaseComObject(oParag); oParag = null;
                if (oRange != null) Marshal.FinalReleaseComObject(oRange); oRange = null;
                if (oTable != null) Marshal.FinalReleaseComObject(oTable); oTable = null;
                if (oTable1 != null) Marshal.FinalReleaseComObject(oTable1); oTable1
= null;
                if (doc != null) Marshal.FinalReleaseComObject(doc); doc = null;
                if (docs != null) Marshal.FinalReleaseComObject(docs); docs = null;
                if (word != null) Marshal.FinalReleaseComObject(word); word = null;
            }

        }


Ошибка стала возникать после добавления в приложение работы с Word

Скриншот ошибки:



Сведения:


  Сигнатура проблемы:
  Имя события проблемы: APPCRASH
  Имя приложения: Explorer.EXE
  Версия приложения: 6.1.7601.17514
  Отметка времени приложения: 4ce7a144
  Имя модуля с ошибкой: ntdll.dll
  Версия модуля с ошибкой: 6.1.7601.18247
  Отметка времени модуля с ошибкой: 521eaf24
  Код исключения: c0000005
  Смещение исключения: 0000000000028389
  Версия ОС: 6.1.7601.2.1.0.256.48
  Код языка: 1049
  Дополнительные сведения 1: 0056
  Дополнительные сведения 2: 00565d7ce61ebbab216279960a6dd5b6
  Дополнительные сведения 3: 2480
  Дополнительные сведения 4: 2480f0afc22eea9532586bc57f7117f6

    


Ответы

Ответ 1



Проводник просто так не падает от работы с WinForms и Word Automation. Типично проблема связана с багами в его расширениях. Чтобы выявить источник ошибки, попробуйте следующее: Включить формирование дампов по инструкции Настроить студию на использование серверов символов Microsoft (Параметры - Отладка - Символы - Microsoft Symbol Servers) После ошибки найти файл с расширением dmp в %LOCALAPPDATA%\CrashDumps, открыть в студии и войти в отладку Изучите стек вызовов в момент падения, какие функции были вызваны и из каких DLL. Нужно смотреть на функции, расположенные ниже системных вызовов из ntdll и kernel32, это выглядит так: Если они относятся к стороннему ПО, попробуйте удалить или переустановить его. Если они относятся к ПО от Microsoft, нужно искать соответствующее исправление или сообщать о баге, если его нет. Можно также использовать утилиту ShellExView для просмотра расширений проводника и отключения ненужных/багнутых.

вторник, 10 декабря 2019 г.

Как вставить таблицу в документ Word на место текстовой метки?

#c_sharp #com #ms_word


Обновление: вопрос закрыт. К черту всё это. В ТЗ нет чётких указаний, поэтому заместо
таблицы вставлю текстовый список.

Имеется таблица в виде объекта DataTable. Нужно вставить её на место определённого
набора символов в документе-шаблоне.

Замену текст-на-текст можно выполнить так (куча missingObj нужан для обхода багов):

    using Word = Microsoft.Office.Interop.Word;

    Word._Application application;
    Word._Document document;
    Object missingObj = System.Reflection.Missing.Value;
    Object trueObj = true;
    Object falseObj = false;

    private void create_button1_Click(object sender, EventArgs e) {
            application = new Word.Application();
            Object templatePathObj;
            templatePathObj = "template.dot";  

            try {
                document = application.Documents.Add(ref  templatePathObj, 
                    ref missingObj, ref missingObj, ref missingObj);
            }
            catch (Exception error) {
                document.Close(ref falseObj, ref  missingObj, ref missingObj);
                application.Quit(ref missingObj, ref  missingObj, ref missingObj);
                document = null;
                application = null;
                throw error;
            }

            object strToFindObj = "%метка%";
            object replaceStrObj = "текст для вставки";
            Word.Range wordRange;
            object replaceTypeObj;
            replaceTypeObj = Word.WdReplace.wdReplaceAll;
            for (int i = 1; i <= document.Sections.Count; i++) {
                wordRange = document.Sections[i].Range;
                Word.Find wordFindObj = wordRange.Find;
                object[] wordFindParameters = new object[15] { strToFindObj, missingObj, 
                    missingObj, missingObj, missingObj, missingObj, missingObj, missingObj, 
                    missingObj, replaceStrObj, replaceTypeObj, missingObj, missingObj, 
                    missingObj, missingObj };
                wordFindObj.GetType().InvokeMember("Execute", BindingFlags.InvokeMethod, 
                    null, wordFindObj, wordFindParameters);
        }
        application.Visible = true;
    }


Как следует скорректировать этот код, чтобы он принимал на вход таблицу и вносил
её в нужное место в документе?

В этом примере замена происходит внутри объекта Range, который представляет собой
фрагмент документа, который может полноценно описывать содержимое документа Word (таблицы,
форматирование).

UPD: Внесу ясность. В моей программе формируется объект DataTable, содержащий таблицу.
Нужно внутри документа Word вставить эту таблицу на место набора символов %метка%.
    


Ответы

Ответ 1



Начиная с .Net 4.0 улучшена поддержка COM и теперь все вызовы можно писать короче (без missingObj и ref): using Word = Microsoft.Office.Interop.Word; using System.Data; var dataTable = new DataTable(); // создаем и заполняем таблицу Word._Application wordApplication = new Word.Application(); Word._Document wordDocument = null; wordApplication.Visible = true; var templatePathObj = @"template.dot"; try { wordDocument = wordApplication.Documents.Add(templatePathObj); } catch (Exception exception) { if (wordDocument != null) { wordDocument.Close(false); wordDocument = null; } wordApplication.Quit(); wordApplication = null; throw; } wordApplication.Selection.Find.Execute("%метка%"); Word.Range wordRange = wordApplication.Selection.Range; var wordTable = wordDocument.Tables.Add(wordRange, dataTable.Rows.Count, dataTable.Columns.Count); for (var j = 0; j < dataTable.Rows.Count; j++) { for (var k = 0; k < dataTable.Columns.Count; k++) { wordTable.Cell(j + 1, k + 1).Range.Text = dataTable.Rows[j][k].ToString(); } }

Ответ 2



Исходная задача не решается с помощью слияния данных MS Excel и MS Word?

среда, 27 ноября 2019 г.

Склонения слов на PHP

#php #ms_word


Есть такая небольшая статистика

"N" Человек просят сейчас помощь!

if (n == 1) => "1 человек просит сейчас помощь"  
elseif ($n == 2) => "2 человекА просЯт сейчас помощь"


и так далее.

Как это сделать?
    


Ответы

Ответ 1



function declOfNum($num, $titles) { $cases = array(2, 0, 1, 1, 1, 2); return $num . " " . $titles[($num % 100 > 4 && $num % 100 < 20) ? 2 : $cases[min($num % 10, 5)]]; } echo declOfNum(5, array('человек просит', 'человека просят', 'человек просят'));

Ответ 2



Вот тут люди обновляют правила для разных языков: Language Plural Rules. Для русского языка: one → n mod 10 is 1 and n mod 100 is not 11; few → n mod 10 in 2..4 and n mod 100 not in 12..14; many → n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14; other → everything else Таким образом: function pluralCategory($count) { $mod10 = $count % 10; $mod100 = $count % 100; if (is_int($count) && $mod10 == 1 && $mod100 != 11) { return 'one'; } elseif (($mod10 > 1 && $mod10 < 5) && ($mod100 < 12 || $mod100 > 14)) { return 'few'; } elseif ($mod10 == 0 || ($mod10 > 4 && $mod10 < 10) || ($mod100 > 10 && $mod100 < 15)) { return 'many'; } else { return 'other'; } } Само собой, в ответе будет только категория. Для слова "человек" массив соответствий будет таким: one → человек few → человека many → человек other → человека Демо: $word = array( 'one' => 'человек', 'few' => 'человека', 'many' => 'человек', 'other' => 'человека', ); for ($i = 1; $i < 500; $i++) { echo $i.' '.$word[pluralCategory($i)].'
'; }

Ответ 3



Вариант для данного случая избыточен, но он подходит и для других случаев, когда у слова не две, а три формы: for( $n = 1; $n < 4096; $n++ ) { $plural = ( $n % 10 == 1 && $n % 100 != 11 ? 'человек' : ( $n % 10 >= 2 && $n % 10 <= 4 && ( $n % 100 < 10 || $n % 100 >= 20 ) ? 'человека' : 'человек' ) ); print "$n $plural\n"; }

Ответ 4



Отмечусь тоже. Скрестил алгоритм @lampa со своими шаблонами. Мне кажется так удобнее. Кода на много строк из-за свободной записи регулярного выражения, поэтому просто дам ссылку на реализацию с тестами: http://ideone.com/XQgK2g В функцию sklonit надо просто передать текст содержащий сколько угодно шаблонов: // шаблон 1 [число:начало(конец1,конец2,конец3)] // начало+конец [2:яблок(о,а,)] // яблока // шаблон 2 число[разделитель:начало(конец1,конец2,конец3)] // число+разделитель+начало+конец 2[ :яблок(о,а,)] // 2 яблока При этом если во втором шаблоне разделитель это число, то считается, что это шаблон первого типа, то есть 1[2:яблок(о,а,)] // 1яблока , а не 12яблоко, если воспринимать это как шаблон 2

Ответ 5



А можно воспользоваться нормальной библиотекой - https://github.com/wapmorgan/Morphos (http://morphos.io) , которая сама поймёт как нужно склонять любое слово. Вы просто пишете morphos\Russian\pluralize(10, 'комментарий') и получаете "10 комментариев"

воскресенье, 9 июня 2019 г.

Создание акта в word C#

Доброго времени суток. Нужно создать акт в word на основе данных, имеющихся на winforms. Подскажите, как лучше это сделать или с чего вообще начать? На листе будет отформатированный текст и таблица, число строк в которой каждый раз будет разным, но не более 10. Примерный внешний вид акта можно увидеть на фото ниже. Места, где замазано, как раз надо будет заменить на свои. Ну и, как писал выше, добавить строки в таблицу. В изначальном шаблоне есть только шапка таблицы, без строк.


Ответ

Используй стандартное расширение Microsoft.Office.Interop.Word и подготовь шаблон с данного документа с закладками в требуемых местах. Краткий код работы с документом:
Word._Document document; Word._Application application=new Word.Application(); object missingObj = Missing.Value; object templatePathObj = documentpath; //открытие файла try { document = application.Documents.Add(ref templatePathObj, ref missingObj, ref missingObj, ref missingObj); } catch { document.Close(ref falseobj, ref missingObj, ref missingObj); application.Quit(ref missingObj, ref missingObj, ref missingObj); document = null; application = null; }
//заполнение закладок object bookmarkObj = "закладка"; Word.Range bookmarkRange = document.Bookmarks.get_Item(ref bookmarkObj).Range; bookmarkRange.Text = "";
//работа с таблицей Word.Table _table = _document.Tables[tableNumber]; //Выбрать уже существующую таблицу внутри документа можно по ее порядковому номеру (начиная с 1 и начала документа) _table.Rows.Add(ref _missingObj); Word.Range _currentRange = _table.Cell(rowIndex, columnIndex).Range; _currentRange.Text="";
//Вывод на печать document.PrintOut(ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj, ref missingObj);
document.Close(Word.WdSaveOptions.wdDoNotSaveChanges); application.Quit();
Подробно об этом расширении есть тут и тут.

среда, 24 апреля 2019 г.

Почему Word открывается в режиме только для чтения

Не могу понять в чем дело. Вроде бы скопировал у себя же рабочий код, но почему-то не работает. А не работает потому что word открывается в режиме для чтения. Хотя перед этим всё работало.
private void button5_Click(object sender, EventArgs e) { //показываем панель для введения значений в бланк panel2.Visible = true; //скрываем изображение pictureBox1.Visible = false;
//Создаём новый Word.Application Microsoft.Office.Interop.Word.Application app = new Microsoft.Office.Interop.Word.Application();
//Загружаем документ Microsoft.Office.Interop.Word.Document doc = null;
//вариант открытия существующего файла object fileName = "E:\\R1.rtf"; object falseValue = false; object trueValue = false; object missing = Type.Missing;
doc = app.Documents.Open(ref fileName, ref missing, ref trueValue, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing);
//Теперь у нас есть документ который мы будем менять.
//Очищаем параметры поиска app.Selection.Find.ClearFormatting(); app.Selection.Find.Replacement.ClearFormatting();
//Задаём параметры замены и выполняем замену нп поле варианта object findText = ""; object replaceWith = VarApply; object replace = 2;
app.Selection.Find.Execute(ref findText, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref replaceWith, ref replace, ref missing, ref missing, ref missing, ref missing); app.Visible = false;
doc.Save(); doc.Close(Microsoft.Office.Interop.Word.WdSaveOptions.wdSaveChanges); app.Quit(false); richTextBox1.LoadFile("E:\\R1.rtf");
}
Не хочет сохранять изменённый файл в строке doc.Save()


Ответ

doc = app.Documents.Open(ref fileName, ref missing, ref trueValue, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing);
Третий параметр - ReadOnly, поставьте его missing.
Да и вообще можно написать кратко
app.Documents.Open(ref fileName)

вторник, 16 октября 2018 г.

Как вставить таблицу в документ Word на место текстовой метки?

Обновление: вопрос закрыт. К черту всё это. В ТЗ нет чётких указаний, поэтому заместо таблицы вставлю текстовый список.
Имеется таблица в виде объекта DataTable. Нужно вставить её на место определённого набора символов в документе-шаблоне.
Замену текст-на-текст можно выполнить так (куча missingObj нужан для обхода багов):
using Word = Microsoft.Office.Interop.Word;
Word._Application application; Word._Document document; Object missingObj = System.Reflection.Missing.Value; Object trueObj = true; Object falseObj = false;
private void create_button1_Click(object sender, EventArgs e) { application = new Word.Application(); Object templatePathObj; templatePathObj = "template.dot";
try { document = application.Documents.Add(ref templatePathObj, ref missingObj, ref missingObj, ref missingObj); } catch (Exception error) { document.Close(ref falseObj, ref missingObj, ref missingObj); application.Quit(ref missingObj, ref missingObj, ref missingObj); document = null; application = null; throw error; }
object strToFindObj = "%метка%"; object replaceStrObj = "текст для вставки"; Word.Range wordRange; object replaceTypeObj; replaceTypeObj = Word.WdReplace.wdReplaceAll; for (int i = 1; i <= document.Sections.Count; i++) { wordRange = document.Sections[i].Range; Word.Find wordFindObj = wordRange.Find; object[] wordFindParameters = new object[15] { strToFindObj, missingObj, missingObj, missingObj, missingObj, missingObj, missingObj, missingObj, missingObj, replaceStrObj, replaceTypeObj, missingObj, missingObj, missingObj, missingObj }; wordFindObj.GetType().InvokeMember("Execute", BindingFlags.InvokeMethod, null, wordFindObj, wordFindParameters); } application.Visible = true; }
Как следует скорректировать этот код, чтобы он принимал на вход таблицу и вносил её в нужное место в документе?
В этом примере замена происходит внутри объекта Range, который представляет собой фрагмент документа, который может полноценно описывать содержимое документа Word (таблицы, форматирование).
UPD: Внесу ясность. В моей программе формируется объект DataTable, содержащий таблицу. Нужно внутри документа Word вставить эту таблицу на место набора символов %метка%


Ответ

Начиная с .Net 4.0 улучшена поддержка COM и теперь все вызовы можно писать короче (без missingObj и ref):
using Word = Microsoft.Office.Interop.Word; using System.Data;
var dataTable = new DataTable(); // создаем и заполняем таблицу
Word._Application wordApplication = new Word.Application(); Word._Document wordDocument = null; wordApplication.Visible = true;
var templatePathObj = @"template.dot";
try { wordDocument = wordApplication.Documents.Add(templatePathObj); } catch (Exception exception) { if (wordDocument != null) { wordDocument.Close(false); wordDocument = null; } wordApplication.Quit(); wordApplication = null; throw; }
wordApplication.Selection.Find.Execute("%метка%"); Word.Range wordRange = wordApplication.Selection.Range;
var wordTable = wordDocument.Tables.Add(wordRange, dataTable.Rows.Count, dataTable.Columns.Count);
for (var j = 0; j < dataTable.Rows.Count; j++) { for (var k = 0; k < dataTable.Columns.Count; k++) { wordTable.Cell(j + 1, k + 1).Range.Text = dataTable.Rows[j][k].ToString(); } }

вторник, 2 октября 2018 г.

Склонения слов на PHP

Есть такая небольшая статистика
"N" Человек просят сейчас помощь!
if (n == 1) => "1 человек просит сейчас помощь" elseif ($n == 2) => "2 человекА просЯт сейчас помощь"
и так далее.
Как это сделать?


Ответ

function declOfNum($num, $titles) { $cases = array(2, 0, 1, 1, 1, 2);
return $num . " " . $titles[($num % 100 > 4 && $num % 100 < 20) ? 2 : $cases[min($num % 10, 5)]]; }
echo declOfNum(5, array('человек просит', 'человека просят', 'человек просят'));